question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Handler override carrying over between tests when it shouldn't

See original GitHub issue

Environment

Name Version
msw 0.28.2
browser Chrome
OS Windows 10

Request handlers

I am writing unit tests for my App component in my React app and I encountered an issue. In this component, I render different child components based on if the user is logged in or not. In the useEffect hook, I make an API call to authenticate the user which occurs when the component first mounts.

To test this functionality I have 2 tests. In the first test I override the handler for ‘/api/refresh-token’ in order to throw a 401 error, so that only some child components are rendered. In the second test, I want to use the handler that I defined in my test setup which will render some different child components.

My test setup for MSW is the following:

src/test/server.ts
import { rest } from 'msw';
import { setupServer } from 'msw/node';

const handlers = [
  rest.post('http://localhost:9000/api/users/signin', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ accessToken: 'abc', user: { id: '1', version: 0 } })
    );
  }),
  rest.post('http://localhost:9000/api/refresh-token', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ accessToken: 'abc', user: { id: '1', version: 0 } })
    );
  }),
];

// This configures a request mocking server with the given request handlers
const server = setupServer(...handlers);

export { server, rest };
src/setupTests.ts
import { server } from './test/server';

// Establish API mocking before all tests.
beforeAll(() => server.listen());

// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers());

// Clean up after the tests are finished.
afterAll(() => server.close());

Actual request

This is my App component.

import { MuiThemeProvider } from '@material-ui/core/styles';
import styles from './App.module.css';
import React, { useEffect, useContext } from 'react';
import theme from './theme';
import { BrowserRouter } from 'react-router-dom';
import { Route, Switch, Redirect } from 'react-router-dom';
import Models from './components/Models/Models';
import FileManagement from './components/FileManagement/FileManagement';
import Navigation from './components/Navigation/Navigation';
import Login from './components/Login/Login';
import { AppContext, ActionTypes } from './context/app-context';
import { axios } from './api/axios';
import Job from './components/Job/Job';

const App = () => {
  const { dispatch } = useContext(AppContext);
  const accessToken = localStorage.getItem('access-token');

  // When refreshing the website, if the refresh token is present in a cookie
  // and still valid, authenticate automatically the user
  useEffect(() => {
    const refreshRequest = async () => {
      const response = await axios.post('/api/refresh-token');
      const { accessToken, user } = response.data;
      localStorage.setItem('access-token', accessToken);
      dispatch({ type: ActionTypes.SignIn, payload: user });
    };

    refreshRequest();
  }, []);

  return (
    <MuiThemeProvider theme={theme}>
      <BrowserRouter>
        {accessToken ? (
          <div>
            <Navigation />
            <div className={styles.container}>
              <Switch>
                <Route path="/models" component={Models} />
                <Route path="/enrichment" component={FileManagement} />
                <Route path="/job/:jobId" component={Job} />
                <Route path="/">
                  <Redirect to="/models" />
                </Route>
              </Switch>
            </div>
          </div>
        ) : (
          <div>
            <Switch>
              <Route exact path="/login" component={Login} />
              <Route path="/">
                <Redirect to="/login" />
              </Route>
            </Switch>
          </div>
        )}
      </BrowserRouter>
    </MuiThemeProvider>
  );
};

export default App;

This is my App.test.ts file.

import App from './App';
import { render, screen, waitFor } from './test/setup';
import { server, rest } from './test/server';

// Mock setItem from localStorage to check if was called
const setItemMock = jest.spyOn(localStorage, 'setItem');

describe('Testing App component', () => {
  it('renders the Login component if user is not logged in', async () => {
    // Get back error message from server
    server.use(
      rest.post('http://localhost:9000/api/refresh-token', (req, res, ctx) => {
        return res(ctx.status(401), ctx.json({ message: 'Not authorized' }));
      })
    );

    render(<App />);

    await waitFor(() => {
      const emailInput = screen.getByRole('textbox', { name: 'Email Address' });
      expect(emailInput).toHaveValue('');
      expect(emailInput).toBeInTheDocument();
      expect(setItemMock).not.toHaveBeenCalledWith('access-token', 'abc');
    });
  });

  it('renders all components except Login component if user is logged in', async () => {
    render(<App />);

    await waitFor(() => {
      const emailInput = screen.getByText(/models/i);
      expect(emailInput).toBeInTheDocument();
      expect(setItemMock).toHaveBeenCalledWith('access-token', 'abc');
    });
  });
});

Current behavior

The first test works as expected: I get a 401 error and the child components that I expect are being rendered. However in the second test, I am getting a 401 error again even though I should be using the handler defined in the test setup which gives a 200.

I printed the handlers using server.printHandlers() and I only see the 2 handlers from the test setup so I shouldn’t be getting the 401 error.

Here is the log from the failing second test.

image

Can you help me figure out what is happening?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Awesome94commented, Sep 5, 2022

@kettanaito After much debugging, I figured out what I was doing wrong. It has nothing to do with msw and it is working fine. Thank you for your time, I will be closing the issue.

@LuisBarroso37, hope I’m not too late to the party. Kindly do you mind sharing what the issue was and how you managed to fix it?

1reaction
LuisBarroso37commented, May 21, 2021

Hey @kettanaito,

I tried to use --runInBand but the issue still occurred. I will try to create a reproduction repo so you can take a look.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I write a Junit test for a Overide method
Seems like you are mocking the class undertest : MessageHandler hand = Mockito.spy(MessageHandler.class); doNothing().when(hand).
Read more >
Best practices for writing unit tests - .NET - Microsoft Learn
Learn best practices for writing unit tests that drive code quality and resilience for .NET Core and .NET Standard projects.
Read more >
Unit Tests, How to Write Testable Code, and Why It Matters
Writing unit tests can be tough, but it shouldn't be. If your tests are hard to write, you probably have problems elsewhere. Untestable...
Read more >
Best Practices for Unit Testing in Kotlin - Philipp Hauer's Blog
Best practices and guidelines for idiomatic and readable unit tests in Kotlin.
Read more >
Testing tools - Django documentation
from django.test import Client >>> c = Client() >>> response ... don't have to worry about state (such as cookies) carrying over from...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found