Mock React Router Components in Testing
Testing React applications often requires isolating components from
their routing context to prevent external dependencies from breaking
unit tests. This article provides a straightforward guide on how to mock
route components, navigation hooks, and router contexts in React using
Jest and React Testing Library. You will learn the practical code
patterns needed to mock react-router-dom hooks like
useNavigate and useParams, as well as how to
mock entire route-rendered components.
Why Mock Route Components?
When unit testing a component that interacts with React Router,
rendering the actual router can introduce unnecessary complexity.
Mocking allows you to: * Isolate behavior: Test the
component’s internal logic without worrying about path matching or
navigation state. * Control outputs: Mock return values
from hooks like useParams to test different scenarios
(e.g., viewing a specific user ID). * Assert side
effects: Verify if navigation functions like
navigate() were called with the correct arguments.
Method 1: Mocking react-router-dom Hooks
The most common requirement is mocking hooks like
useNavigate, useParams, or
useLocation. You can mock the entire
react-router-dom module using Jest.
Here is how to mock useNavigate to assert that a user is
redirected on a button click:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
// 1. Create a mock navigate function
const mockNavigate = jest.fn();
// 2. Mock the react-router-dom module
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockNavigate,
}));
test('navigates to dashboard on button click', () => {
render(<MyComponent />);
const button = screen.getByRole('button', { name: /go to dashboard/i });
fireEvent.click(button);
// 3. Assert that the navigate function was called with the correct path
expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
});To mock useParams to simulate a specific URL parameter
(like an ID):
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({ userId: '12345' }),
}));Method 2: Mocking a Custom Route Component
If your component renders a child component that relies heavily on a route context, you can mock that child component entirely to bypass the router requirement.
Suppose you have a Dashboard component that renders a
complex UserProfileRoute component. You can mock the child
component in your test file:
import { render, screen } from '@testing-library/react';
import Dashboard from './Dashboard';
// Mock the child component to render a simple placeholder
jest.mock('./UserProfileRoute', () => {
return function MockUserProfile() {
return <div data-testid="mock-profile">Mocked User Profile Route</div>;
};
});
test('renders dashboard with mocked profile route', () => {
render(<Dashboard />);
expect(screen.getByTestId('mock-profile')).toBeInTheDocument();
expect(screen.getByText('Mocked User Profile Route')).toBeInTheDocument();
});Method 3: Using MemoryRouter (Alternative to Mocking)
If you prefer not to mock the router hooks or components, you can
wrap your component in MemoryRouter from
react-router-dom. This is the recommended approach for
integration tests where you want to test actual routing behavior.
import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import MyComponent from './MyComponent';
test('renders correct content based on route', () => {
render(
<MemoryRouter initialEntries={['/user/99']}>
<Routes>
<Route path="/user/:id" element={<MyComponent />} />
</Routes>
</MemoryRouter>
);
// Assert against the rendered output of MyComponent with parameter id = 99
expect(screen.getByText('User ID: 99')).toBeInTheDocument();
});