useNavigate — это хук, предоставляемый библиотекой
react-router-dom, который позволяет программно изменять
маршрут в приложении. Его основная цель — это навигация между
страницами, без необходимости взаимодействовать с UI напрямую. В
процессе тестирования React-компонентов важно научиться правильно
проверять работу маршрутизации, особенно когда приложение использует
useNavigate для смены маршрутов.
useNavigateДля эффективного тестирования компонентов, использующих хук
useNavigate, необходимо понять, как правильно использовать
и симулировать его действия. Обычно это делается с помощью имитации
навигации, проверки правильности вызова функции и определения того, как
компонент реагирует на изменения маршрута.
useNavigateПоскольку useNavigate является хуком, предоставляемым
react-router-dom, он не может быть напрямую использован или
проверен в тестах без мокирования. В стандартных тестах, где не
используется реальный роутер, его нужно замокать, чтобы можно было
отслеживать вызовы навигации и тестировать их в контексте конкретного
компонента.
import { render, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';
// Мокаем хук useNavigate
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: jest.fn(),
}));
test('проверка навигации при клике на кнопку', () => {
const navigate = jest.fn(); // Мокаем функцию navigate
require('react-router-dom').useNavigate.mockReturnValue(navigate);
const { getByText } = render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
fireEvent.click(getByText(/Перейти/)); // Имитируем клик по кнопке
expect(navigate).toHaveBeenCalledWith('/новый-маршрут'); // Проверяем, что navigate был вызван с правильным маршрутом
});
В этом примере jest.mock используется для замокания хука
useNavigate. В тесте мы создаем мок-функцию
navigate, которую передаем в компонент через мокированный
хук. Это позволяет проверить, была ли навигация выполнена правильно при
взаимодействии с компонентом.
MemoryRouter для тестирования навигацииЧтобы протестировать навигацию, важно обернуть компонент в
MemoryRouter. Это позволяет эмулировать изменения маршрута
в тестах без необходимости устанавливать полноценный роутер.
MemoryRouter сохраняет состояние маршрутов в памяти, что
делает его идеальным для тестов.
import { render, fireEvent } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import MyComponent from './MyComponent';
test('проверка смены маршрута', () => {
const { getByText } = render(
<MemoryRouter initialEntries={['/']}>
<Routes>
<Route path="/" element={<MyComponent />} />
<Route path="/новый-маршрут" element={<div>Новый маршрут</div>} />
</Routes>
</MemoryRouter>
);
fireEvent.click(getByText(/Перейти/)); // Имитируем клик по кнопке
expect(getByText(/Новый маршрут/)).toBeInTheDocument(); // Проверяем, что новый маршрут отобразился
});
В этом тесте используется MemoryRouter с маршрутом
/. После клика по кнопке, которая вызывает навигацию,
проверяется, что контент компонента с новым маршрутом появился на
экране.
Когда тестируется компонент с хук-навигатором, важно проверять, что
навигация действительно происходит. Это можно сделать с помощью
отслеживания изменений состояния роутера. Использование
MemoryRouter позволяет контролировать начальные и конечные
маршруты, а также позволяет анализировать, была ли осуществлена
навигация по ожидаемому пути.
test('проверка изменения маршрута при навигации', () => {
const { getByText } = render(
<MemoryRouter initialEntries={['/']}>
<Routes>
<Route path="/" element={<MyComponent />} />
<Route path="/новый-маршрут" element={<div>Новый маршрут</div>} />
</Routes>
</MemoryRouter>
);
fireEvent.click(getByText(/Перейти/)); // Имитируем клик по кнопке
expect(window.location.pathname).toBe('/новый-маршрут'); // Проверяем, что путь изменился
});
В данном случае тест проверяет, что после нажатия на кнопку
Перейти путь в браузере изменится на
/новый-маршрут.
Если компонент, использующий хук useNavigate, вызывает
навигацию после выполнения асинхронной операции (например, после
загрузки данных), то для теста потребуется использовать асинхронные
методы, такие как waitFor или findBy.
import { render, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';
test('проверка навигации после асинхронной операции', async () => {
const navigate = jest.fn();
require('react-router-dom').useNavigate.mockReturnValue(navigate);
const { getByText } = render(
<MemoryRouter>
<MyComponent />
</MemoryRouter>
);
fireEvent.click(getByText(/Загрузить и перейти/)); // Имитируем клик по кнопке
await waitFor(() => expect(navigate).toHaveBeenCalledWith('/новый-маршрут')); // Ожидаем, что навигация произойдет после асинхронной операции
});
Здесь тестируем компонент, который выполняет асинхронную операцию
перед тем, как выполнить навигацию. Мы используем waitFor,
чтобы дождаться завершения асинхронного действия.
useNavigate в более сложных компонентахКогда компонент, использующий useNavigate, является
частью более сложной логики (например, связанной с контекстом или
другими хуками), можно замокать только необходимые части, оставив
остальную часть компонента без изменений.
import { render, fireEvent } from '@testing-library/react';
import { MyContextProvider } from './MyContext';
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(
<MyContextProvider>
<MyComponent />
</MyContextProvider>
);
fireEvent.click(getByText(/Перейти с контекстом/));
expect(navigate).toHaveBeenCalledWith('/новый-маршрут');
});
В этом примере компонент, использующий useNavigate,
оборачивается в провайдер контекста, что добавляет дополнительную
сложность. Мокирование хука useNavigate позволяет проверить
его работу, даже если компонент зависит от контекста.
Тестирование компонентов, использующих useNavigate,
требует правильного мокирования самого хука и использования обертки
маршрутизатора, такой как MemoryRouter, для эмуляции работы
с маршрутами. Важно понимать, что для разных типов взаимодействий
(клики, асинхронные действия, изменения контекста) могут потребоваться
различные подходы для обеспечения точности тестирования.