Мокирование навигации

Мокирование навигации в React Router используется для тестирования компонентов, которые зависят от маршрутов, без необходимости запускать полноценное приложение с реальным роутером. Это особенно важно при написании unit-тестов с использованием библиотек вроде Jest и React Testing Library, где цель — изолированно проверить логику компонента.

Использование MemoryRouter

Вместо обычного <BrowserRouter> в тестах применяется <MemoryRouter>. Этот компонент хранит историю навигации в памяти и позволяет задавать начальный путь, что делает тестирование детерминированным.

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

test('рендер компонента на определенном маршруте', () => {
  render(
    <MemoryRouter initialEntries={['/about']}>
      <App />
    </MemoryRouter>
  );

  expect(screen.getByText(/О нас/i)).toBeInTheDocument();
});
  • initialEntries — массив путей, по которым будет симулироваться история навигации.
  • Это позволяет проверить корректность рендеринга компонентов на разных маршрутах.

Мокирование useNavigate

Функция useNavigate используется для программной навигации. В тестах её можно замокать с помощью Jest, чтобы проверять вызовы без реальной смены маршрута.

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

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: jest.fn(),
}));

test('навигация при клике на кнопку', () => {
  const navigate = jest.fn();
  useNavigate.mockReturnValue(navigate);

  const { getByText } = render(<MyComponent />);
  fireEvent.click(getByText('Перейти'));

  expect(navigate).toHaveBeenCalledWith('/target');
});
  • Мокирование позволяет проверить параметры вызова функции navigate, не изменяя реальный URL.
  • Полезно при тестировании компонентов с условной логикой навигации.

Мокирование параметров маршрута с useParams

Компоненты часто используют useParams для получения динамических сегментов URL. Для тестов этот хук также можно замокать.

import { render } from '@testing-library/react';
import { useParams } from 'react-router-dom';
import UserPage from './UserPage';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: jest.fn(),
}));

test('отображение данных пользователя', () => {
  useParams.mockReturnValue({ id: '42' });

  const { getByText } = render(<UserPage />);
  expect(getByText(/Пользователь 42/i)).toBeInTheDocument();
});
  • Это позволяет имитировать разные URL-параметры без изменения истории браузера.
  • В сочетании с моками fetch или axios можно полностью тестировать страницы с динамическим контентом.

Мокирование useLocation

Хук useLocation возвращает объект текущего пути, состояния и поисковых параметров. Его можно замокать для проверки компонентов, которые реагируют на изменение URL.

import { render } from '@testing-library/react';
import { useLocation } from 'react-router-dom';
import SearchPage from './SearchPage';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useLocation: jest.fn(),
}));

test('отрисовка результатов поиска', () => {
  useLocation.mockReturnValue({
    pathname: '/search',
    search: '?query=react',
    state: null,
    hash: ''
  });

  const { getByText } = render(<SearchPage />);
  expect(getByText(/Результаты поиска по react/i)).toBeInTheDocument();
});
  • Позволяет тестировать реакцию компонента на query-параметры и состояние навигации.
  • Не требуется запускать полноценный роутер или изменять URL браузера.

Для unit-тестов можно замокать компоненты <Link> и <NavLink> так, чтобы они не изменяли историю браузера, а только регистрировали клик:

import { render, fireEvent } from '@testing-library/react';
import { Link } from 'react-router-dom';
import Navigation from './Navigation';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  Link: ({ children, to, ...props }) => (
    <a href={to} {...props}>{children}</a>
  ),
}));

test('клики по ссылкам', () => {
  const { getByText } = render(<Navigation />);
  fireEvent.click(getByText('Главная'));

  // Проверка поведения при клике можно делать через callback или spy
});
  • Позволяет тестировать визуальные и логические аспекты ссылок без реальной навигации.
  • Удобно при проверке условного отображения ссылок в зависимости от состояния приложения.

Практические рекомендации

  1. Использовать MemoryRouter для тестирования компонентов с вложенными маршрутами.
  2. Мокировать useNavigate, useParams и useLocation для проверки бизнес-логики без реального браузера.
  3. Избегать BrowserRouter в unit-тестах, чтобы исключить побочные эффекты и ошибки при запуске в Node-среде.
  4. Комбинировать мокирование с spy-функциями Jest, чтобы контролировать вызовы навигации и их аргументы.
  5. Тестировать переходы через пользовательские события, а не только через программные вызовы navigate.

Мокирование навигации позволяет создавать полностью изолированные тесты компонентов, проверять их поведение на разных маршрутах, с разными параметрами и состояниями без запуска полноценного приложения. Это повышает скорость тестирования и надежность проверки логики, особенно при сложных маршрутах и вложенной навигации.