'Simplest test for react-router's Link with @testing-library/react
I'm trying to understand how to best test that react-router behaves as expected with @testing-library/react.
The simplest test I can think of, is to verify that clicking a link changes the URL. I know that ideally I should test that clicking on a link renders a new component, but that adds a lot of boilerplate to the test.
So here's my failing example:
import { MemoryRouter } from 'react-router-dom';
import { render } from '@testing-library/react';
import { createMemoryHistory } from 'history';
it('routes to a new route', async () => {
const history = createMemoryHistory();
const { getByText } = render(
<MemoryRouter history={history}>
<Link to="/hello">Click me</Link>
</MemoryRouter>
);
fireEvent.click(getByText('Click me'));
await waitFor(() => expect(history.location.pathname).to.equal('/hello')); // Fails
});
Solution 1:[1]
This is how I do it : mocking history.push
, then spy on its calls.
import { MemoryRouter } from 'react-router-dom';
import { render } from '@testing-library/react';
import { createMemoryHistory } from 'history';
it('routes to a new route', async () => {
const history = createMemoryHistory();
// mock push function
history.push = jest.fn();
const { getByText } = render(
<MemoryRouter history={history}>
<Link to="/hello">Click me</Link>
</MemoryRouter>
);
// could be userEvent.click
// https://testing-library.com/docs/ecosystem-user-event/#clickelement-eventinit-options
fireEvent.click(getByText('Click me'));
// spy on push calls, assert on url (parameter)
expect(history.push).toHaveBeenCalledWith('/hello');
});
Solution 2:[2]
With Typescript and "react-router-dom": "^6.3.0"
, the <MemoryRouter />
component does not accept the history prop.
This doesn't work <MemoryRouter history={history} >
I have worked around it with the example below.
import { render, screen, fireEvent } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { Router, Link } from 'react-router-dom';
it('routes to a new route', () => {
const history = createMemoryHistory();
history.push = jest.fn();
render(
<Router location={history.location} navigator={history}>
<Link to="/hello">Click Me</Link>
</Router>,
);
fireEvent.click(screen.getByText(/Click Me/i));
expect(history.push).toHaveBeenCalledWith(
{
hash: '',
pathname: '/hello',
search: '',
},
undefined,
);
});
Solution 3:[3]
You do not have to use link to change the URL, you can simply use Route.
Sample:
it('should change the url', () => {
expect.assertions(1);
act(() => {
render(
<BrowserRouter>
<ProtectedRoute path="/mockPage" component={MyPage} />
<Route render={() => <Redirect to="/mockPage" />} />
</BrowserRouter>
);
});
expect(screen.getByTestId('an-element-in-myPage')).toBeInTheDocument();
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Pierre |
Solution 3 | user3184564 |