How to Mock MemoryRouter in React

Testing React components that rely on routing requires a simulated router environment to prevent runtime errors. This article explains how to use and mock MemoryRouter from react-router-dom in your unit tests, enabling you to test navigation, route parameters, and location-dependent rendering without a real browser.

Why Use MemoryRouter in Tests?

When a component uses React Router hooks like useNavigate, useLocation, or useParams, rendering it in isolation during a test will throw an error. These hooks require a router context to function.

Instead of using the standard BrowserRouter, which relies on the browser’s history API, you use MemoryRouter. It stores its locations internally in an array, making it ideal for Node.js-based testing environments like Jest or Vitest.

Wrapping Components in MemoryRouter

The most common way to mock the routing environment is to wrap the component under test inside MemoryRouter. You can use the initialEntries prop to set the starting URL path.

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/123']}>
      <Routes>
        <Route path="/user/:id" element={<UserProfile />} />
      </Routes>
    </MemoryRouter>
  );

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

Mocking the useNavigate Hook

If your goal is to verify that a component successfully triggers a redirect or navigates to a new page, wrapping the component in MemoryRouter is not always enough to assert how navigation was called. In this case, you should mock the useNavigate hook directly.

Here is how to mock the navigation hook using Jest:

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

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

// Mock react-router-dom and spy on useNavigate
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: () => mockNavigate,
}));

test('calls navigate when button is clicked', () => {
  render(<MyComponent />);
  
  const button = screen.getByRole('button', { name: /go home/i });
  fireEvent.click(button);

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

Creating a Reusable Render Helper

If many components in your test suite require a router context, you can create a custom render function to avoid repeating the MemoryRouter wrapper code.

import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';

const renderWithRouter = (ui, { route = '/' } = {}) => {
  window.history.pushState({}, 'Test page', route);

  return render(
    <MemoryRouter initialEntries={[route]}>
      {ui}
    </MemoryRouter>
  );
};

// Usage in tests:
test('renders with custom helper', () => {
  renderWithRouter(<MyComponent />, { route: '/dashboard' });
  expect(screen.getByText(/Dashboard/i)).toBeInTheDocument();
});