При тестировании компонентов React, которые взаимодействуют с
внешними API через fetch, важно изолировать логику
приложения от реальных сетевых запросов. Библиотека
jest-fetch-mock позволяет полностью контролировать
ответы fetch и создавать надежные тесты без зависимости от
сервера.
Для начала необходимо установить пакет:
npm install --save-dev jest-fetch-mock
После установки нужно подключить его в конфигурации Jest. Наиболее
удобный способ — использовать файл setupTests.js:
import fetchMock from 'jest-fetch-mock';
fetchMock.enableMocks();
Опция enableMocks() заменяет глобальный
fetch на мок-реализацию, доступную во всех тестах.
Важно убедиться, что Jest действительно загружает
setupTests.js. В package.json это делается
через поле:
"jest": {
"setupFilesAfterEnv": ["<rootDir>/src/setupTests.js"]
}
После настройки fetch становится мок-функцией, которую
можно конфигурировать:
beforeEach(() => {
fetch.resetMocks();
});
test('возвращает данные от API', async () => {
fetch.mockResponseOnce(JSON.stringify({ id: 1, name: 'Test' }));
const response = await fetch('/api/data');
const data = await response.json();
expect(data).toEqual({ id: 1, name: 'Test' });
expect(fetch).toHaveBeenCalledWith('/api/data');
});
Ключевые моменты:
fetch.resetMocks() очищает предыдущие моки, что
предотвращает «утечки» между тестами.mockResponseOnce позволяет задать конкретный ответ на
один вызов fetch.fetch с помощью
toHaveBeenCalledWith гарантирует, что запрос был отправлен
корректно.Для тестирования обработки ошибок API используется
mockRejectOnce:
test('обрабатывает ошибку API', async () => {
fetch.mockRejectOnce(new Error('Сетевая ошибка'));
try {
await fetch('/api/data');
} catch (error) {
expect(error.message).toBe('Сетевая ошибка');
}
expect(fetch).toHaveBeenCalledTimes(1);
});
Это полезно для проверки логики компонента при сбоях сети или некорректных данных.
Можно задать стандартный ответ для всех вызовов fetch,
если один тест покрывает несколько запросов:
fetch.mockResponse(JSON.stringify({ success: true }));
test('несколько запросов', async () => {
const response1 = await fetch('/api/one');
const data1 = await response1.json();
const response2 = await fetch('/api/two');
const data2 = await response2.json();
expect(data1).toEqual({ success: true });
expect(data2).toEqual({ success: true });
});
Разница между mockResponseOnce и
mockResponse:
mockResponseOnce — влияет только на один вызов.mockResponse — задает поведение для всех вызовов до
следующей очистки мока.Для тестирования компонентов, реагирующих на разные коды ответа, используется объект с опциями:
fetch.mockResponseOnce(JSON.stringify({ message: 'Not found' }), { status: 404 });
const response = await fetch('/api/missing');
expect(response.status).toBe(404);
const data = await response.json();
expect(data).toEqual({ message: 'Not found' });
Таким образом, можно проверять обработку ошибок на уровне HTTP.
В React Testing Library часто тестируют асинхронное поведение
компонентов после запроса fetch:
import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';
test('отображает данные пользователя', async () => {
fetch.mockResponseOnce(JSON.stringify({ id: 1, name: 'Alice' }));
render(<UserProfile userId={1} />);
await waitFor(() => expect(screen.getByText('Alice')).toBeInTheDocument());
});
Особенности:
waitFor позволяет дождаться обновления DOM после
асинхронного запроса.fetch.mockResponseOnce гарантирует, что компонент
получит предсказуемые данные.Можно задать функцию вместо статического ответа для имитации разных данных в зависимости от URL или метода:
fetch.mockResponse(req => {
if (req.url.endsWith('/users')) {
return Promise.resolve(JSON.stringify([{ id: 1, name: 'Alice' }]));
}
return Promise.resolve(JSON.stringify({ message: 'Not found' }));
});
const response = await fetch('/users');
const data = await response.json();
expect(data).toEqual([{ id: 1, name: 'Alice' }]);
Такой подход полезен для сложных сценариев с несколькими endpoint’ами.
mockResponseOnce(body, options) — один ответ на один
вызов.mockResponse(body, options) — одинаковый ответ для всех
вызовов.mockRejectOnce(error) — отклоняет один вызов с
ошибкой.mockReject(error) — отклоняет все последующие
вызовы.resetMocks() — очищает состояние всех моков, включая
вызовы и ответы.enableMocks() — подключает мок вместо глобального
fetch.Эти методы позволяют создавать детализированные тесты, полностью контролируя сетевое поведение компонентов React.
beforeEach(fetch.resetMocks)).mockResponseOnce для отдельных тестов,
чтобы исключить перекрестное влияние.waitFor или findBy из React
Testing Library.toHaveBeenCalledWith.Эта практика обеспечивает стабильные и предсказуемые тесты, полностью изолированные от внешних API и сети.