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();
});
});