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
});
You can use the createMemoryHistory function and Router component to test it. Create a memory history with initial entries to simulate the current location, this way we don't rely on the real browser environment. After firing the click event, assert the pathname is changed correctly or not.
Link.test.js import React from 'react'; import renderer from 'react-test-renderer'; import { Link } from 'react-router-dom'; test('Link matches snapshot', () => { const component = renderer. create( <Link to="#" /> ); let tree = component. toJSON(); expect(tree). toMatchSnapshot(); });
To add the link in the menu, use the <NavLink /> component by react-router-dom . The NavLink component provides a declarative way to navigate around the application. It is similar to the Link component, except it can apply an active style to the link if it is active.
So your React app, like a lot of modern apps, uses React Router to get users from one page to another. And you, like any thorough tester, want to account for React Router in your testing.
No wonder Jest is confused. As luck would have it, Testing Library makes it easy to adapt its render function to wrap with whatever your UI elements might need — be it the React Router provider, or any other type of provider (see “Including the Router and other Providers” below for rendering with multiple providers).
As luck would have it, Testing Library makes it easy to adapt its render function to wrap with whatever your UI elements might need — be it the React Router provider, or any other type of provider (see “Including the Router and other Providers” below for rendering with multiple providers).
It turns out, when you remove components from the React Router Switch component, you don’t have access to the match object (which contains the URL params, along with other route information). You can fix this by using useRouteMatch in Locations.jsx instead of useParams:
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');
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With