How to Test BrowserRouter in React

Testing components that rely on React Router’s BrowserRouter can be challenging because they depend on the browser’s history API, which is not fully available in standard testing environments like Jest and JSDOM. This article explains how to successfully test routing in React by replacing BrowserRouter with MemoryRouter in your test suite, providing clear, practical code examples to get your tests passing.

The Problem with BrowserRouter in Tests

When you run tests using React Testing Library and Jest, the code executes in a simulated browser environment (JSDOM). If you try to render a component that uses React Router hooks (like useNavigate, useParams, or useLocation) or components (like <Link /> or <Route />) without a router parent, your tests will throw an error: useNavigate() may be used only in the context of a <Router> component.

Directly wrapping your test components in BrowserRouter is also problematic because it couples your tests to the actual browser’s URL, making it difficult to isolate test cases or simulate specific route paths.

The Solution: MemoryRouter

The recommended way to test React Router components is to use MemoryRouter instead of BrowserRouter. MemoryRouter stores its locations internally in an array, making it ideal for testing environments where there is no actual browser address bar.

Step 1: The Component to Test

Here is a simple Sidebar component that uses React Router’s <Link> to navigate:

// Sidebar.jsx
import { Link } from 'react-router-dom';

export function Sidebar() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/profile">Profile</Link>
    </nav>
  );
}

Step 2: Writing the Test with MemoryRouter

To test this component, wrap it inside <MemoryRouter> within your render function. This provides the routing context required by the <Link> components.

// Sidebar.test.jsx
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Sidebar } from './Sidebar';

describe('Sidebar Component', () => {
  it('renders navigation links successfully', () => {
    render(
      <MemoryRouter>
        <Sidebar />
      </MemoryRouter>
    );

    const homeLink = screen.getByRole('link', { name: /home/i });
    const profileLink = screen.getByRole('link', { name: /profile/i });

    expect(homeLink).toBeInTheDocument();
    expect(profileLink).toBeInTheDocument();
    expect(profileLink).toHaveAttribute('href', '/profile');
  });
});

Testing Specific Routes and Navigation

If your component behaves differently based on the current URL path, you can initialize MemoryRouter with specific entries and indexes using the initialEntries prop.

// UserProfile.jsx
import { useParams } from 'react-router-dom';

export function UserProfile() {
  const { username } = useParams();
  return <h1>User: {username}</h1>;
}

By wrapping the target component in both MemoryRouter and <Routes>, you can simulate being on a specific route:

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

describe('UserProfile Component', () => {
  it('displays the correct username from the URL path', () => {
    render(
      <MemoryRouter initialEntries={['/user/john_doe']}>
        <Routes>
          <Route path="/user/:username" element={<UserProfile />} />
        </Routes>
      </MemoryRouter>
    );

    expect(screen.getByRole('heading', { name: /user: john_doe/i })).toBeInTheDocument();
  });
});