Skip to content

Unit testing React Router

React Router lets us navigate between pages using the <Link/> component and through hooks like useNavigate. When we are testing the functionality of React Router, we usually want to ensure that clicking on an element navigates the user to the appropriate screen. The main thing that we should focus on is that our component is navigating to the correct URL path. The component of that is rendered after the user is navigated should not be the responsibility of the component which is navigating.

We can do this by rendering our component inside of a <Routes> component that is only used for our test cases. We can usually render to <Route/> components: one for the component we want to test and a dummy component which we expect to see after navigating.

render(
<MemoryRouter>
<Routes>
<Route path="/" element={<UserCard user={user} />} />
<Route path={`/user/${user.id}`} element={<p>Dummy page</p>} />
</Routes>
</MemoryRouter>,
);

Using a <MemoryRouter/> also resolves the following error:

Terminal window
Error: useRoutes() may be used only in the context of a <Router> component.

Afterwards, we can simulate the user clicking on the link in the component and asserting that the “Dummy page” component is visible.

// retrieves the link element from the rendered component.
const linkToUserDetails = screen.getByRole('link');
// simulates a user click on the link.
await user.click(linkToUserDetails);
// checks whether the text "Dummy page" is visible on the screen. This is asserting that after clicking the link, the user is navigated to the page with the dummy text.
expect(screen.getByText('Dummy page')).toBeVisible();

Examples#

import { userEvent } from '@testing-library/user-event';
import { render } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { UserCard } from './UserCard';
import { userData } from '../data/userData';
describe('UserCard', () => {
it('should link to the user details page', async () => {
const user = userEvent.setup();
const screen = render(
<MemoryRouter>
<Routes>
<Route path="/" element={<UserCard user={userData[0]} />} />
<Route path={`/user/${userData[0].id}`} element={<p>Dummy page</p>} />
</Routes>
</MemoryRouter>,
);
const linkToUserDetails = screen.getByRole('link');
await user.click(linkToUserDetails);
expect(screen.getByText('Dummy page')).toBeVisible();
});
});

Test navigation with useNavigate#

import { render } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { UserDetails } from "./UserDetails";
import { userData } from "../data/userData";
describe("UserDetails", () => {
it("should navigate back to / when clicking button", async () => {
const user = userEvent.setup();
const screen = render(
<MemoryRouter initialEntries={[`/user/${userData[0].id}`]}>
<Routes>
<Route path="/user/:userId" element={<UserDetails />} />
<Route path="/" element={<p>Dummy page</p>} />
</Routes>
</MemoryRouter>
);
const buttonToClick = screen.getByRole("button");
await user.click(buttonToClick);
expect(screen.getByText("Dummy page")).toBeVisible();
});
});

Useful resources#

  • Source code can be found here.
  • React Testing library also maintains its own documentation on testing React Router.