How to Test React Router Routes

Testing routing in React ensures that your application navigates correctly and renders the appropriate components when URLs change. This article provides a straightforward guide on how to test React Router routes using React Testing Library and Jest or Vitest. You will learn how to set up testing environments using MemoryRouter, mock navigation, and assert that the correct pages load based on user interaction or initial URL states.


Why Use MemoryRouter for Testing?

When testing React applications that use React Router, you cannot easily use the standard BrowserRouter because it relies on the browser’s history API, which is not available in a Node.js testing environment (like Jest or Vitest).

Instead, React Router provides MemoryRouter. This router stores its locations internally in an array, making it ideal for testing routing behavior without a real browser window.


Testing Static Routes

To test if a specific route renders the correct component, wrap the component under test in a MemoryRouter and use the initialEntries prop to set the starting URL path.

The Component under Test

// App.jsx
import { Routes, Route, Link } from 'react-router-dom';

export const Home = () => <div>Home Page</div>;
export const About = () => <div>About Page</div>;
export const NotFound = () => <div>Page Not Found</div>;

export const App = () => (
  <div>
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
    </nav>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  </div>
);

The Test File

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

describe('App Routing', () => {
  test('renders Home Page on default route', () => {
    render(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>
    );

    expect(screen.getByText(/Home Page/i)).toBeInTheDocument();
  });

  test('renders About Page when URL is /about', () => {
    render(
      <MemoryRouter initialEntries={['/about']}>
        <App />
      </MemoryRouter>
    );

    expect(screen.getByText(/About Page/i)).toBeInTheDocument();
  });

  test('renders Page Not Found on a bad route', () => {
    render(
      <MemoryRouter initialEntries={['/this-route-does-not-exist']}>
        <App />
      </MemoryRouter>
    );

    expect(screen.getByText(/Page Not Found/i)).toBeInTheDocument();
  });
});

Testing User Navigation

To test that clicking a link successfully navigates the user to a new route, use user-event to simulate a mouse click and assert that the new component appears in the DOM.

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

test('navigates to About Page when the link is clicked', async () => {
  render(
    <MemoryRouter initialEntries={['/']}>
      <App />
    </MemoryRouter>
  );

  // Verify we start on the home page
  expect(screen.getByText(/Home Page/i)).toBeInTheDocument();

  // Click the About link
  const aboutLink = screen.getByRole('link', { name: /about/i });
  await userEvent.click(aboutLink);

  // Verify the URL change rendered the About component
  expect(screen.getByText(/About Page/i)).toBeInTheDocument();
});

Best Practices for Routing Tests

  1. Keep components isolated: If you only want to test a single page’s behavior, wrap just that page component in MemoryRouter.
  2. Use initialEntries: Always explicitly define the initial route entry so your tests are predictable and do not rely on implicit defaults.
  3. Avoid mocking the router: Whenever possible, test the actual router integration rather than mocking react-router-dom functions. This guarantees that your configuration works in production.