How to Test Route Components in React

Testing route components in React ensures that your application navigates correctly and renders the correct user interface based on the URL. This guide provides a straightforward approach to testing React Router components using Jest and React Testing Library, focusing on the use of MemoryRouter to simulate routing behaviors in a test environment.

Why Use MemoryRouter for Testing?

When testing React components that use React Router (like <Link>, <Route>, or routing hooks), rendering them directly will cause errors because they expect to be wrapped in a router context.

While your live application uses BrowserRouter to sync with the browser’s address bar, tests run in a Node.js environment where no real browser address bar exists. To solve this, React Router provides MemoryRouter. It stores the URL history in memory, allowing you to manually define the active route using the initialEntries prop.

Setting Up a Route Test

To test routing, wrap your routing component inside MemoryRouter within your test file. Below is an example of a routing component and its corresponding test.

The Component (AppRoutes.js)

import { Routes, Route, Link } from 'react-router-dom';

const Home = () => <div>Home Page</div>;
const About = () => <div>About Page</div>;

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

The Test (AppRoutes.test.js)

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

describe('AppRoutes Navigation', () => {
  test('renders the Home page by default', () => {
    render(
      <MemoryRouter initialEntries={['/']}>
        <AppRoutes />
      </MemoryRouter>
    );

    expect(screen.getByText(/home page/i)).toBeInTheDocument();
  });

  test('renders the About page when navigating to /about', () => {
    render(
      <MemoryRouter initialEntries={['/about']}>
        <AppRoutes />
      </MemoryRouter>
    );

    expect(screen.getByText(/about page/i)).toBeInTheDocument();
  });
});

Testing User Navigation

To verify that clicking a navigation link actually changes the route and displays the new component, you can simulate user clicks using @testing-library/user-event.

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

test('navigates to the about page when the link is clicked', async () => {
  render(
    <MemoryRouter initialEntries={['/']}>
      <AppRoutes />
    </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 UI updates to show the About page
  expect(screen.getByText(/about page/i)).toBeInTheDocument();
});

Handling Route Parameters

If your route components rely on URL parameters (such as /users/:id), you can pass the parameterized path directly into the initialEntries array of the MemoryRouter.

For a component with the path /users/:id:

render(
  <MemoryRouter initialEntries={['/users/123']}>
    <AppRoutes />
  </MemoryRouter>
);

Inside your component, the useParams hook will correctly retrieve { id: '123' }, allowing you to test dynamic data fetching or UI state changes tied to route variables.