MemoryRouter для тестов

MemoryRouter — это специальный тип роутера из библиотеки react-router-dom, который используется преимущественно в тестах и в сценариях, где не требуется работа с настоящим URL браузера. В отличие от BrowserRouter, который синхронизирует состояние маршрутов с адресной строкой, MemoryRouter хранит историю навигации в памяти, что делает его идеальным для изолированного тестирования компонентов с маршрутизацией.

Основные особенности MemoryRouter

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

  2. Начальные маршруты Через проп initialEntries можно задать массив путей, которые будут использоваться в качестве стартовой истории навигации.

    <MemoryRouter initialEntries={['/login']}>
      <App />
    </MemoryRouter>

    В этом примере компонент App будет рендериться так, как если бы пользователь сразу перешёл на /login.

  3. Начальный индекс Проп initialIndex позволяет указать, какой путь из initialEntries использовать как текущий.

    <MemoryRouter initialEntries={['/home', '/profile']} initialIndex={1}>
      <App />
    </MemoryRouter>

    Здесь текущий путь будет /profile, хотя /home находится в истории.

  4. Контроль истории Объект истории можно получить через useNavigate или useLocation внутри компонента, что позволяет тестировать навигацию программно. Например:

    import { render, screen } from '@testing-library/react';
    import { MemoryRouter, Routes, Route, useNavigate } from 'react-router-dom';
    
    function Login() {
      const navigate = useNavigate();
      return <button onCl ick={() => navigate('/dashboard')}>Войти</button>;
    }
    
    test('переход на dashboard после клика', () => {
      render(
        <MemoryRouter initialEntries={['/login']}>
          <Routes>
            <Route path="/login" element={<Login />} />
            <Route path="/dashboard" element={<div>Dashboard</div>} />
          </Routes>
        </MemoryRouter>
      );
    
      screen.getByText('Войти').click();
      expect(screen.getByText('Dashboard')).toBeInTheDocument();
    });

Советы по тестированию с MemoryRouter

  • Тестирование редиректов: Использование initialEntries позволяет задавать стартовый маршрут и проверять, корректно ли компонент выполняет перенаправление.
  • Тестирование вложенных маршрутов: MemoryRouter поддерживает любую вложенность маршрутов через компонент Routes. Это удобно при проверке сложной структуры приложения.
  • Проверка состояния навигации: С помощью хуков useLocation и useNavigationType можно тестировать, какой маршрут был посещён, и какие действия навигации выполнялись (PUSH, REPLACE, POP).

Важные нюансы

  1. Синхронизация с React Testing Library MemoryRouter идеально сочетается с render из @testing-library/react, так как оба инструмента предназначены для изолированного тестирования компонентов.

  2. События навигации Поскольку навигация полностью контролируется памятью, нельзя тестировать эффекты, связанные с изменением адресной строки в браузере (например, событие popstate). Все такие сценарии нужно эмулировать вручную через initialEntries или программное управление историей.

  3. Комбинация с пользовательскими хуками Компоненты, использующие useNavigate, useParams или useLocation, корректно работают внутри MemoryRouter, что позволяет полностью эмулировать поведение роутинга без запуска браузера.

Примеры практического применения

1. Тест редиректа после авторизации

function AuthRoute({ isAuthenticated, children }) {
  return isAuthenticated ? children : <Navigate to="/login" />;
}

test('редирект на login при неавторизованном доступе', () => {
  render(
    <MemoryRouter initialEntries={['/dashboard']}>
      <Routes>
        <Route path="/dashboard" element={<AuthRoute isAuthenticated={false}><div>Dashboard</div></AuthRoute>} />
        <Route path="/login" element={<div>Login</div>} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText('Login')).toBeInTheDocument();
});

2. Тест навигации между страницами

function Navbar() {
  const navigate = useNavigate();
  return (
    <>
      <button onCl ick={() => navigate('/')}>Главная</button>
      <button onCl ick={() => navigate('/about')}>О нас</button>
    </>
  );
}

test('переход на страницу О нас', () => {
  render(
    <MemoryRouter initialEntries={['/']}>
      <Routes>
        <Route path="/" element={<Navbar />} />
        <Route path="/about" element={<div>About Page</div>} />
      </Routes>
    </MemoryRouter>
  );

  screen.getByText('О нас').click();
  expect(screen.getByText('About Page')).toBeInTheDocument();
});

MemoryRouter обеспечивает предсказуемую и контролируемую среду для тестирования компонентов с маршрутизацией. Его ключевые преимущества — отсутствие зависимости от браузера, возможность задавать начальные маршруты и удобная интеграция с React Testing Library.