How to Mock React Router in Jest

Testing components that rely on React Router requires simulating routing behavior without running a full browser environment. This article provides a straightforward guide on how to mock React Router in your Jest tests, covering both the integration testing approach using MemoryRouter and the unit testing approach using Jest module mocks to spy on hooks like useNavigate and useParams.

The most robust way to test components that use React Router is to wrap them in the MemoryRouter component provided by react-router-dom. This avoids actual mocking and instead provides a real, in-memory routing context to your components.

This approach is ideal when your component contains links or route-matching logic.

import { render, screen } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import UserProfile from './UserProfile';

test('renders user profile based on route parameter', () => {
  render(
    <MemoryRouter initialEntries={['/user/42']}>
      <Routes>
        <Route path="/user/:id" element={<UserProfile />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText(/User ID: 42/i)).toBeInTheDocument();
});

Approach 2: Mocking Hooks with jest.mock()

If you only need to assert that a routing hook was called (for example, verifying that a button click redirects the user), you can mock the react-router-dom library directly using Jest.

Mocking useNavigate

To assert that useNavigate was called with the correct path, mock the hook at the top of your test file.

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useNavigate } from 'react-router-dom';
import MyButton from './MyButton';

// Create a mock navigation function
const mockNavigate = jest.fn();

// 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', async () => {
  render(<MyButton />);
  
  const button = screen.getByRole('button', { name: /go to dashboard/i });
  await userEvent.click(button);

  expect(mockNavigate).toHaveBeenCalledWith('/dashboard');
});

Mocking useParams

If you want to mock URL path parameters without setting up a full router structure in your tests, you can mock useParams to return a specific object.

import { render, screen } from '@testing-library/react';
import ProductDetails from './ProductDetails';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => ({ productId: 'abc-123' }),
}));

test('displays the product ID from URL params', () => {
  render(<ProductDetails />);
  expect(screen.getByText(/Product: abc-123/i)).toBeInTheDocument();
});