Handler override carrying over between tests when it shouldn't
See original GitHub issueEnvironment
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.
Can you help me figure out what is happening?
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (3 by maintainers)
Top GitHub Comments
@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?
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.