How to Test Route Components in React
Testing route components in React ensures that your application
navigates correctly and renders the correct user interface based on the
URL. This guide provides a straightforward approach to testing React
Router components using Jest and React Testing Library, focusing on the
use of MemoryRouter to simulate routing behaviors in a test
environment.
Why Use MemoryRouter for Testing?
When testing React components that use React Router (like
<Link>, <Route>, or routing
hooks), rendering them directly will cause errors because they expect to
be wrapped in a router context.
While your live application uses BrowserRouter to sync
with the browser’s address bar, tests run in a Node.js environment where
no real browser address bar exists. To solve this, React Router provides
MemoryRouter. It stores the URL history in memory, allowing
you to manually define the active route using the
initialEntries prop.
Setting Up a Route Test
To test routing, wrap your routing component inside
MemoryRouter within your test file. Below is an example of
a routing component and its corresponding test.
The Component
(AppRoutes.js)
import { Routes, Route, Link } from 'react-router-dom';
const Home = () => <div>Home Page</div>;
const About = () => <div>About Page</div>;
export const AppRoutes = () => (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
);The Test
(AppRoutes.test.js)
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { AppRoutes } from './AppRoutes';
describe('AppRoutes Navigation', () => {
test('renders the Home page by default', () => {
render(
<MemoryRouter initialEntries={['/']}>
<AppRoutes />
</MemoryRouter>
);
expect(screen.getByText(/home page/i)).toBeInTheDocument();
});
test('renders the About page when navigating to /about', () => {
render(
<MemoryRouter initialEntries={['/about']}>
<AppRoutes />
</MemoryRouter>
);
expect(screen.getByText(/about page/i)).toBeInTheDocument();
});
});Testing User Navigation
To verify that clicking a navigation link actually changes the route
and displays the new component, you can simulate user clicks using
@testing-library/user-event.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import { AppRoutes } from './AppRoutes';
test('navigates to the about page when the link is clicked', async () => {
render(
<MemoryRouter initialEntries={['/']}>
<AppRoutes />
</MemoryRouter>
);
// Verify we start on the Home page
expect(screen.getByText(/home page/i)).toBeInTheDocument();
// Click the About link
const aboutLink = screen.getByRole('link', { name: /about/i });
await userEvent.click(aboutLink);
// Verify the UI updates to show the About page
expect(screen.getByText(/about page/i)).toBeInTheDocument();
});Handling Route Parameters
If your route components rely on URL parameters (such as
/users/:id), you can pass the parameterized path directly
into the initialEntries array of the
MemoryRouter.
For a component with the path /users/:id:
render(
<MemoryRouter initialEntries={['/users/123']}>
<AppRoutes />
</MemoryRouter>
);Inside your component, the useParams hook will correctly
retrieve { id: '123' }, allowing you to test dynamic data
fetching or UI state changes tied to route variables.