Тестирование API-вызовов
Тестирование API-вызовов — проверка кода, который делает HTTP-запросы, через мокирование fetch/axios (unit) или реальные запросы к тест-серверу (integration).
Зачем нужно
Реальные HTTP-запросы в unit-тестах делают их медленными, нестабильными и зависимыми от внешних сервисов. Мокирование позволяет проверить все сценарии: успех, ошибка сети, 404, 500 — предсказуемо и быстро. Integration-тест с реальным сервером проверяет корректность взаимодействия.
Где используется
- Unit-тесты сервисов, работающих с API
- Тестирование React компонентов с data-fetching
- Integration-тесты Node.js API с supertest
- Тестирование retry-логики и обработки ошибок
Основной контент
Мокирование fetch через jest.mock
// api.js
export async function getUser(id) {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP error: ${res.status}`);
return res.json();
}
// api.test.js
global.fetch = jest.fn;
afterEach(() => {
jest.clearAllMocks;
});
test('возвращает данные пользователя', async () => {
fetch.mockResolvedValue({
ok: true,
json: async => ({ id: 1, name: 'Alice' }),
});
const user = await getUser(1);
expect(fetch).toHaveBeenCalledWith('/api/users/1');
expect(user.name).toBe('Alice');
});
test('выбрасывает ошибку при 404', async () => {
fetch.mockResolvedValue({ ok: false, status: 404 });
await expect(getUser(999)).rejects.toThrow('HTTP error: 404');
});
Мокирование axios
// userService.test.ts
import axios from 'axios';
import { getUser } from './userService';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
test('получает пользователя', async () => {
mockedAxios.get.mockResolvedValue({ data: { id: 1, name: 'Alice' } });
const user = await getUser(1);
expect(mockedAxios.get).toHaveBeenCalledWith('/api/users/1');
expect(user.name).toBe('Alice');
});
test('выбрасывает ошибку при сетевой ошибке', async () => {
mockedAxios.get.mockRejectedValue(new Error('Network Error'));
await expect(getUser(1)).rejects.toThrow('Network Error');
});
MSW (Mock Service Worker) — перехват на уровне сети
npm install --save-dev msw
// mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/users/:id', ({ params }) => {
return HttpResponse.json({ id: Number(params.id), name: 'Alice' });
}),
http.post('/api/users', async ({ request }) => {
const body = await request.json();
return HttpResponse.json({ id: 1, ...body }, { status: 201 });
}),
http.get('/api/users/999', () => {
return HttpResponse.json({ error: 'Not found' }, { status: 404 });
}),
];
// mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// jest.setup.ts
import { server } from './mocks/server';
beforeAll( => server.listen);
afterEach( => server.resetHandlers);
afterAll( => server.close());
Integration-тест Node.js API с supertest
// users.router.test.ts
import request from 'supertest';
import { app } from '../app';
import { db } from '../db';
beforeEach(async () => { await db('users').truncate; });
afterAll(async () => { await db.destroy(); });
test('GET /users возвращает список', async () => {
await db('users').insert([{ name: 'Alice' }, { name: 'Bob' }]);
const res = await request(app).get('/users').expect(200);
expect(res.body).toHaveLength(2);
expect(res.body[0].name).toBe('Alice');
});
Частые ошибки
- Реальные HTTP в unit-тестах — зависимость от сети делает тесты нестабильными; всегда мокируй fetch/axios в unit-тестах
- Нет теста на сетевую ошибку — проверяй не только успешный ответ, но и
Network Error, таймаут, 5xx - Mock не соответствует реальному ответу — если структура mock-данных отличается от реального API, тест бесполезен; синхронизируй с типами
Связанные темы
- _MOC Тестирование
- Jest -- моки (jest.fn, jest.mock)
- Jest -- тестирование асинхронного кода
- Integration тесты
- Моки и стабы