Тестирование запросов с axios, fetch

Основы тестирования HTTP-запросов

В React-приложениях взаимодействие с сервером часто реализуется через axios или встроенный fetch. Тестирование таких компонентов требует изоляции бизнес-логики от реальных сетевых запросов, чтобы тесты оставались быстрыми, предсказуемыми и не зависели от состояния сервера. Основные подходы — мокирование и проверка вызовов.

  • Мокирование позволяет подменить реальный HTTP-запрос фиктивной функцией, которая возвращает заранее определённый результат.
  • Проверка вызовов позволяет убедиться, что функция запроса была вызвана с правильными параметрами и обработана корректно.

Мокирование axios

axios — это библиотека, возвращающая промисы, что делает её удобной для мокирования с помощью Jest.

Пример мокирования запроса:

import axios from 'axios';
import { render, screen, waitFor } from '@testing-library/react';
import UsersList from './UsersList';

jest.mock('axios');

test('загружает и отображает список пользователей', async () => {
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];

  axios.get.mockResolvedValue({ data: users });

  render();

  // Ожидание, пока элементы появятся в DOM
  for (const user of users) {
    await waitFor(() => expect(screen.getByText(user.name)).toBeInTheDocument());
  }

  // Проверка, что axios.get был вызван один раз с правильным URL
  expect(axios.get).toHaveBeenCalledWith('/api/users');
});

Ключевые моменты:

  • jest.mock('axios') подменяет все методы axios на мок-функции.
  • mockResolvedValue позволяет имитировать успешный ответ с данными.
  • waitFor используется для асинхронного ожидания изменений в DOM.
  • Проверка вызова axios.get гарантирует корректность параметров запроса.

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

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

Пример с Jest:

import { render, screen, waitFor } from '@testing-library/react';
import UsersList from './UsersList';

beforeEach(() => {
  global.fetch = jest.fn();
});

afterEach(() => {
  jest.resetAllMocks();
});

test('загружает и отображает список пользователей через fetch', async () => {
  const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];

  fetch.mockResolvedValue({
    ok: true,
    json: async () => users,
  });

  render();

  for (const user of users) {
    await waitFor(() => expect(screen.getByText(user.name)).toBeInTheDocument());
  }

  expect(fetch).toHaveBeenCalledWith('/api/users');
});

Особенности мокирования fetch:

  • fetch.mockResolvedValue возвращает объект с полями ok и json(), имитирующий реальный Response.
  • Для имитации ошибок можно использовать mockRejectedValue или ok: false с соответствующей логикой обработки.

Тестирование ошибок запросов

Важно проверять корректную обработку ошибок:

test('отображает сообщение об ошибке при сбое запроса', async () => {
  axios.get.mockRejectedValue(new Error('Network Error'));

  render();

  await waitFor(() => expect(screen.getByText(/ошибка/i)).toBeInTheDocument());
});

Для fetch аналогично:

fetch.mockRejectedValue(new Error('Network Error'));

Основные моменты:

  • Асинхронная обработка ошибок проверяется через waitFor.
  • Тесты гарантируют, что UI реагирует на сбой корректно (показывает сообщение или fallback).

Интеграция с React Testing Library

При тестировании компонентов, использующих axios или fetch:

  1. Мокировать запросы до рендера компонента.

  2. Использовать waitFor или findBy* селекторы для асинхронного ожидания появления данных.

  3. Проверять:

    • Правильность вызова запроса (URL, метод, параметры)
    • Рендер полученных данных
    • Обработку ошибок
  4. Стараться не тестировать сам axios или fetch, а лишь логику компонента, зависящую от данных запроса.


Проверка последовательных вызовов и задержек

Иногда необходимо тестировать несколько последовательных запросов или ожидание:

axios.get
  .mockResolvedValueOnce({ data: [{ id: 1, name: 'Alice' }] })
  .mockResolvedValueOnce({ data: [{ id: 2, name: 'Bob' }] });

render();

await waitFor(() => expect(screen.getByText('Alice')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Bob')).toBeInTheDocument());

Преимущества:

  • Позволяет проверять обработку состояния загрузки между запросами.
  • Поддерживает тестирование компонента при динамически меняющихся данных.

Рекомендации по структуре тестов

  • Arrange: Подготовка моков и рендера компонента.
  • Act: Вызов действий пользователя или рендера.
  • Assert: Проверка отображения данных, вызовов API, состояния ошибок.

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