При тестировании компонентов React, которые взаимодействуют с сервером, важно корректно моделировать динамические ответы. Компоненты могут отправлять HTTP-запросы и ожидать различные результаты: успешные ответы, ошибки или задержки. Неправильное тестирование таких сценариев приводит к нестабильным тестам и ложным срабатываниям.
Для контроля серверных ответов в тестах используют мокирование. Наиболее популярные подходы включают:
jest.mock() для подмены модулей,
выполняющих HTTP-запросы.msw (Mock Service Worker) для имитации
реальных сетевых вызовов на уровне Service Worker.fetch-mock или
axios-mock-adapter при использовании fetch или
axios соответственно.Пример с jest.mock() и fetch:
import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';
global.fetch = jest.fn();
test('отображает данные пользователя после запроса', async () => {
fetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ name: 'Иван', age: 30 }),
});
render(<UserProfile userId={1} />);
const nameElement = await screen.findByText('Иван');
expect(nameElement).toBeInTheDocument();
});
В этом примере ключевые моменты:
mockResolvedValueOnce для задания
конкретного ответа сервера.findByText учитывает
задержку ответа.Тестирование компонентов должно покрывать не только успешные ответы, но и ошибки: сетевые сбои, статус-коды 4xx и 5xx. Для этого создаются отдельные мок-ответы.
test('отображает сообщение об ошибке при сбое запроса', async () => {
fetch.mockRejectedValueOnce(new Error('Сбой сети'));
render(<UserProfile userId={1} />);
const errorElement = await screen.findByText(/Ошибка/i);
expect(errorElement).toBeInTheDocument();
});
Здесь важно:
mockRejectedValueOnce для симуляции
исключений.Иногда компонент делает несколько запросов подряд. Для проверки последовательных сценариев fetch позволяет настроить несколько ответов:
fetch
.mockResolvedValueOnce({ ok: true, json: async () => ({ step: 1 }) })
.mockResolvedValueOnce({ ok: true, json: async () => ({ step: 2 }) });
Это полезно для проверки:
Реальные сетевые запросы имеют задержку, которую также можно эмулировать:
fetch.mockImplementationOnce(() =>
new Promise(resolve =>
setTimeout(() => resolve({ ok: true, json: async () => ({ name: 'Иван' }) }), 500)
)
);
setTimeout позволяет проверить индикаторы
загрузки.waitFor или findBy* селекторы.jest.useFakeTimers() для ускорения
тестов.MSW предоставляет более гибкий способ мокирования, позволяя создавать полноценный имитируемый сервер:
import { setupServer } from 'msw/node';
import { rest } from 'msw';
const server = setupServer(
rest.get('/api/user/:id', (req, res, ctx) => {
return res(ctx.json({ name: 'Иван', age: 30 }));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Преимущества MSW:
server.use().При работе с динамическими ответами важно соблюдать правила:
findBy* и
waitFor.Динамические ответы сервера требуют внимательного подхода к мокированию, асинхронному ожиданию и проверке различных сценариев. Корректная организация таких тестов делает их надежными, быстрыми и легко поддерживаемыми.