Мокирование HTTP запросов

Мокирование HTTP запросов — это одна из ключевых техник, используемых при тестировании приложений, взаимодействующих с внешними сервисами через HTTP. В Jest мокирование позволяет создать фальшивые ответы на запросы, чтобы проверить работу компонента без реального обращения к внешнему серверу. Это ускоряет тесты, делает их независимыми от внешних систем и улучшает стабильность тестового окружения.

Основы мокирования в Jest

Для мокирования HTTP запросов обычно используется библиотека jest.mock() или специализированные инструменты, такие как axios-mock-adapter или fetch-mock. Основной принцип заключается в том, чтобы перехватить HTTP запросы, имитировать ответы и воссоздать поведение реальных запросов.

Пример:

import axios from 'axios';

jest.mock('axios');

test('fetches successfully data from an API', async () => {
  axios.get.mockResolvedValue({ data: 'some data' });

  const response = await axios.get('/some-api');
  expect(response.data).toBe('some data');
});

Здесь axios.get.mockResolvedValue() позволяет задать, что будет возвращено при выполнении HTTP GET запроса, а сам тест проверяет, что полученные данные соответствуют ожидаемому результату.

Мокирование с использованием jest.mock()

Когда необходимо мокировать функцию или модуль, Jest предоставляет функцию jest.mock(). Она позволяет заменить оригинальный модуль на его подделку, которую можно настроить для возврата конкретных значений.

Пример с мокированием HTTP библиотеки axios:

import axios from 'axios';
import { fetchData } from './api';

jest.mock('axios');

test('should fetch data successfully', async () => {
  axios.get.mockResolvedValue({ data: { name: 'John' } });

  const result = await fetchData();
  expect(result.name).toBe('John');
});

В этом примере функция fetchData делает HTTP запрос с помощью axios.get(). Мы мокаем метод get для того, чтобы он возвращал заранее заданное значение, не выполняя реальный запрос.

Мокирование fetch API

Для тестирования компонентов, использующих нативный fetch, можно использовать библиотеку jest-fetch-mock, которая предоставляет аналогичный механизм мокирования для стандартного API.

Пример:

import { fetchData } from './api';

beforeAll(() => {
  global.fetch = jest.fn().mockResolvedValue({
    json: jest.fn().mockResolvedValue({ name: 'John' })
  });
});

test('should fetch data successfully', async () => {
  const result = await fetchData();
  expect(result.name).toBe('John');
});

Здесь мы переопределяем глобальную функцию fetch, используя мок, который возвращает фиктивный объект с методом json(), возвращающим данные.

Мокирование с задержкой или ошибкой

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

Пример мока с задержкой:

jest.mock('axios');
axios.get.mockResolvedValueOnce(new Promise(resolve => setTimeout(() => resolve({ data: 'delayed data' }), 1000)));

test('should fetch data after delay', async () => {
  const data = await axios.get('/some-api');
  expect(data.data).toBe('delayed data');
});

Пример с ошибкой:

jest.mock('axios');
axios.get.mockRejectedValueOnce(new Error('Network Error'));

test('should handle network error', async () => {
  try {
    await axios.get('/some-api');
  } catch (e) {
    expect(e.message).toBe('Network Error');
  }
});

Эти примеры помогают тестировать поведение приложения при реальных задержках или ошибках при выполнении запросов.

Использование jest.spyOn() для мокирования существующих функций

Вместо мока целого модуля можно использовать jest.spyOn(), чтобы подменить конкретный метод на фальшивую реализацию. Это полезно, если необходимо замокировать только одну часть функции.

Пример:

import { fetchData } from './api';

test('should mock axios.get method only', async () => {
  const spy = jest.spyOn(axios, 'get').mockResolvedValue({ data: 'mocked data' });

  const result = await fetchData();
  expect(result.data).toBe('mocked data');

  spy.mockRestore();
});

Здесь мы мокаем только метод get из axios, оставив остальные методы без изменений. Важно помнить, что после теста стоит восстанавливать оригинальную реализацию с помощью mockRestore().

Мокирование сложных запросов

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

Пример:

import axios from 'axios';

jest.mock('axios');

test('should pass correct headers in request', async () => {
  axios.get.mockResolvedValue({ data: 'response' });

  const response = await axios.get('/some-api', {
    headers: { Authorization: 'Bearer token' }
  });

  expect(axios.get).toHaveBeenCalledWith('/some-api', {
    headers: { Authorization: 'Bearer token' }
  });
});

В данном случае тест проверяет, что запрос был отправлен с нужными заголовками.

Управление моки в различных жизненных циклах теста

Чтобы создать правильную изоляцию для каждого теста, важно управлять моками и очищать их состояния между тестами. Jest предоставляет несколько функций для управления этим процессом, включая beforeAll(), beforeEach(), afterAll() и afterEach().

Пример:

beforeEach(() => {
  axios.get.mockClear(); // Очистка моков перед каждым тестом
});

test('should make a successful request', async () => {
  axios.get.mockResolvedValue({ data: 'success' });

  const result = await axios.get('/api');
  expect(result.data).toBe('success');
});

Здесь метод mockClear() очищает состояние мока перед каждым тестом, чтобы избежать взаимодействия между тестами.

Мокирование с асинхронными методами

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

Пример:

jest.mock('axios');
axios.get.mockResolvedValue({ data: 'some data' });

test('should handle async request correctly', async () => {
  const response = await axios.get('/api');
  expect(response.data).toBe('some data');
});

В случае асинхронных функций важно использовать методы, такие как mockResolvedValue() или mockRejectedValue(), чтобы имитировать различные результаты.

Резюме

Мокирование HTTP запросов с помощью Jest предоставляет мощные инструменты для тестирования компонентов, взаимодействующих с внешними сервисами. Оно позволяет избежать реальных сетевых запросов, повышая скорость и стабильность тестов. Важные техники включают использование jest.mock(), jest.spyOn(), а также специализированных библиотек для мокирования HTTP, таких как axios-mock-adapter и jest-fetch-mock. С их помощью можно моделировать успешные, ошибочные запросы, задержки и другие сценарии, что делает тесты более гибкими и реалистичными.