Интеграционное тестирование навигации

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


Настройка тестовой среды

Для интеграционного тестирования навигации необходимо использовать библиотеку для рендеринга компонентов в тестах, например, @testing-library/react, и утилиты для работы с асинхронным поведением, такие как @testing-library/jest-dom и jest. Основная цель – имитировать поведение пользователя и проверять состояние приложения после навигации.

Пример базовой настройки:

import { render, screen } from '@testing-library/react';
import { RouterProvider, createRouter, Route } from '@tanstack/router';
import App from './App';

const router = createRouter({
  routes: [
    new Route({ path: '/', component: Home }),
    new Route({ path: '/about', component: About }),
    new Route({ path: '/profile/:id', component: Profile }),
  ],
});

function renderWithRouter(ui, { route = '/' } = {}) {
  window.history.pushState({}, 'Test page', route);
  return render(<RouterProvider router={router}>{ui}</RouterProvider>);
}

В данном примере создается тестовый роутер с базовыми маршрутами, а вспомогательная функция renderWithRouter позволяет рендерить компоненты в контексте маршрутизатора и задавать стартовый путь.


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

Для проверки навигации необходимо эмулировать действия пользователя: клики по ссылкам, программные переходы через router.navigate, а затем проверять, что отображается корректный компонент.

import userEvent from '@testing-library/user-event';

test('переход на страницу About', async () => {
  renderWithRouter(<App />);
  
  const aboutLink = screen.getByText('About');
  await userEvent.click(aboutLink);

  expect(screen.getByText('Страница About')).toBeInTheDocument();
});

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


Работа с динамическими параметрами маршрутов

TanStack Router поддерживает динамические сегменты, например /profile/:id. Для интеграционного тестирования важно проверять корректность передачи параметров и рендеринг соответствующего контента.

test('отображение профиля пользователя с id', async () => {
  renderWithRouter(<App />, { route: '/profile/42' });

  expect(screen.getByText('Профиль пользователя 42')).toBeInTheDocument();
});

В этом примере проверяется, что маршрут с параметром id корректно подставляется в компонент и отображается ожидаемый текст. Для более сложных сценариев можно использовать мокированные запросы к API и проверять их вызовы.


Асинхронная подгрузка данных

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

import { rest } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  rest.get('/api/profile/42', (req, res, ctx) => {
    return res(ctx.json({ name: 'Иван' }));
  })
);

beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());

test('загрузка данных профиля через loader', async () => {
  renderWithRouter(<App />, { route: '/profile/42' });

  expect(await screen.findByText('Иван')).toBeInTheDocument();
});

findByText используется для ожидания асинхронного рендеринга. Такой подход позволяет интеграционно протестировать полный цикл: навигацию → загрузку данных → отображение контента.


Тестирование вложенных маршрутов

TanStack Router поддерживает вложенные маршруты, что требует проверки корректного рендеринга дочерних компонентов. В тестах важно эмулировать переход к родительскому маршруту с вложенными дочерними маршрутами и проверять отображение всех уровней.

test('вложенные маршруты работают корректно', async () => {
  renderWithRouter(<App />, { route: '/dashboard/settings' });

  expect(screen.getByText('Панель управления')).toBeInTheDocument();
  expect(screen.getByText('Настройки')).toBeInTheDocument();
});

Обработка ошибок и fallback маршрутов

Необходимо проверять сценарии, когда пользователь переходит на несуществующий маршрут или возникает ошибка в loader. TanStack Router позволяет использовать errorComponent или notFoundComponent.

test('неизвестный маршрут отображает 404', async () => {
  renderWithRouter(<App />, { route: '/unknown-route' });

  expect(screen.getByText('Страница не найдена')).toBeInTheDocument();
});

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


Советы по организации интеграционных тестов

  • Использовать вспомогательные функции для рендеринга с роутером.
  • Проверять визуальные признаки компонента, а не внутреннее состояние.
  • Мокировать сетевые запросы для асинхронных маршрутов.
  • Писать отдельные тесты для динамических и вложенных маршрутов.
  • Включать сценарии ошибок, чтобы убедиться в корректной работе fallback-компонентов.

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