Настройка тестового окружения

Для начала работы с React Router необходимо установить соответствующий пакет. На текущий момент актуальной версией является react-router-dom, предназначенная для веб-приложений на React. Установка выполняется через npm или yarn:

npm install react-router-dom
# или
yarn add react-router-dom

После установки важно убедиться, что версия React совместима с React Router. Последние версии требуют React 18 и выше, что обеспечивает поддержку новых возможностей, таких как Suspense и Concurrent Mode.


Импорт основных компонентов

Основные компоненты библиотеки:

  • BrowserRouter — оборачивает приложение и обеспечивает работу с историей браузера.
  • Routes — контейнер для всех маршрутов приложения.
  • Route — отдельный маршрут, связывающий путь с компонентом.
  • Link и NavLink — элементы для навигации без перезагрузки страницы.
  • useNavigate — хук для программной навигации.
  • useParams — хук для получения параметров маршрута.

Пример базового импорта:

import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  NavLink,
  useNavigate,
  useParams
} from 'react-router-dom';

Настройка тестового окружения

Для тестирования маршрутов рекомендуется использовать Jest и React Testing Library. Они позволяют эмулировать поведение пользователя и проверять корректность навигации.

Установка зависимостей для тестов:

npm install --save-dev @testing-library/react @testing-library/jest-dom jest

Настройка окружения для React Router

Чтобы тесты могли корректно обрабатывать маршруты, компоненты должны быть обернуты в MemoryRouter, который имитирует поведение BrowserRouter без участия реального браузера:

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

test('рендерит главную страницу', () => {
  render(
    <MemoryRouter initialEntries={['/']}>
      <App />
    </MemoryRouter>
  );
  expect(screen.getByText(/главная страница/i)).toBeInTheDocument();
});

Ключевой момент — initialEntries позволяет задать начальный путь, по которому будет выполнен рендеринг компонента. Это особенно полезно для проверки маршрутов с параметрами и редиректов.


Тестирование маршрутов с параметрами

React Router поддерживает динамические сегменты в URL, например /users/:id. Для тестов это выглядит так:

import UserPage from './UserPage';
import { MemoryRouter, Route, Routes } from 'react-router-dom';

test('рендерит страницу пользователя по id', () => {
  render(
    <MemoryRouter initialEntries={['/users/42']}>
      <Routes>
        <Route path="/users/:id" element={<UserPage />} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText(/пользователь 42/i)).toBeInTheDocument();
});

Здесь MemoryRouter и initialEntries создают виртуальный URL /users/42, а компонент UserPage использует useParams для получения id.


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

Для тестирования переходов между маршрутами используется хук useNavigate или компонент Link. В тестах можно симулировать клики и проверять обновление URL:

import { fireEvent } from '@testing-library/react';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import App from './App';

test('переход на страницу About', () => {
  render(
    <MemoryRouter initialEntries={['/']}>
      <App />
    </MemoryRouter>
  );

  fireEvent.click(screen.getByText(/о нас/i));
  expect(screen.getByText(/страница о нас/i)).toBeInTheDocument();
});

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


Настройка асинхронных маршрутов

Современные приложения часто используют ленивую загрузку компонентов с помощью React.lazy и Suspense. Для тестирования таких маршрутов необходимо оборачивать компоненты в Suspense:

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

const LazyPage = React.lazy(() => import('./LazyPage'));

test('рендер ленивой страницы', async () => {
  render(
    <MemoryRouter initialEntries={['/lazy']}>
      <Suspense fallback={<div>Загрузка...</div>}>
        <Routes>
          <Route path="/lazy" element={<LazyPage />} />
        </Routes>
      </Suspense>
    </MemoryRouter>
  );

  expect(await screen.findByText(/ленивая страница/i)).toBeInTheDocument();
});

Использование findByText вместо getByText важно для асинхронных компонентов, так как оно возвращает промис и корректно ждёт рендеринга.


Моки и контексты

Для полноценного тестирования маршрутов часто необходимо предоставлять контексты, например AuthContext или ThemeContext. Они оборачиваются вокруг MemoryRouter:

import { AuthProvider } from './AuthContext';

render(
  <AuthProvider value={{ user: { name: 'Иван' } }}>
    <MemoryRouter initialEntries={['/dashboard']}>
      <App />
    </MemoryRouter>
  </AuthProvider>
);

Это обеспечивает корректное функционирование компонентов, зависящих от контекста, в тестовой среде.


Вывод

Тщательная настройка тестового окружения с использованием MemoryRouter, моков контекста и асинхронной поддержки позволяет создавать стабильные и воспроизводимые тесты для приложений на React с React Router. Использование initialEntries, обёртки в Suspense и правильной структуры маршрутов гарантирует точное тестирование логики навигации, параметров URL и редиректов.