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

Мокирование HTTP-запросов является ключевым элементом тестирования компонентов, которые взаимодействуют с внешними API. В экосистеме React Testing Library (RTL) чаще всего используется библиотека axios для выполнения запросов. Прямое выполнение реальных HTTP-запросов во время тестов нежелательно: это снижает их скорость, делает результаты нестабильными и создаёт зависимость от внешних сервисов. Мокирование решает эти проблемы, позволяя контролировать ответы и имитировать ошибки.

Основные подходы к мокированию axios

Существует несколько распространённых способов мокирования axios в тестах:

  1. Использование jest.mock Jest позволяет автоматически подменять модули через jest.mock. Это наиболее простой и часто используемый способ.

    import axios from 'axios';
    import { render, screen, waitFor } from '@testing-library/react';
    import MyComponent from './MyComponent';
    
    jest.mock('axios');
    
    test('отображает данные после запроса', async () => {
      const mockData = { data: { message: 'Привет, мир!' } };
      axios.get.mockResolvedValueOnce(mockData);
    
      render(<MyComponent />);
    
      const element = await screen.findByText(/Привет, мир!/i);
      expect(element).toBeInTheDocument();
    });

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

    • jest.mock('axios') подменяет все методы axios на jest-функции.
    • mockResolvedValueOnce задаёт конкретный ответ для одного вызова.
    • Для эмуляции ошибок используется mockRejectedValueOnce.
  2. Создание отдельного мок-файла При большом проекте удобно вынести моки в отдельный файл. Например:

    // __mocks__/axios.js
    const axios = {
      get: jest.fn(),
      post: jest.fn(),
      put: jest.fn(),
      delete: jest.fn(),
    };
    
    export default axios;

    Jest автоматически подхватывает такой файл при вызове jest.mock('axios'). Это позволяет централизованно управлять всеми HTTP-запросами в тестах.

  3. Использование axios-mock-adapter Для более сложных сценариев, когда нужно мокировать разные эндпоинты и методы с разными ответами, удобна библиотека axios-mock-adapter.

    import axios from 'axios';
    import MockAdapter from 'axios-mock-adapter';
    import { render, screen } from '@testing-library/react';
    import MyComponent from './MyComponent';
    
    test('axios-mock-adapter пример', async () => {
      const mock = new MockAdapter(axios);
      mock.onGet('/api/message').reply(200, { message: 'Привет, мир!' });
    
      render(<MyComponent />);
    
      const element = await screen.findByText(/Привет, мир!/i);
      expect(element).toBeInTheDocument();
    });

    Преимущества axios-mock-adapter:

    • Возможность задавать ответы для конкретных URL и HTTP-методов.
    • Поддержка последовательных ответов для одного запроса.
    • Имитация ошибок через .reply(500) или .networkError().

Асинхронное тестирование с моками

Компоненты, использующие axios, почти всегда асинхронны. В React Testing Library асинхронные операции проверяются с помощью findBy* и waitFor.

Пример обработки ошибки:

test('отображает сообщение об ошибке при неудачном запросе', async () => {
  axios.get.mockRejectedValueOnce(new Error('Сервер недоступен'));

  render(<MyComponent />);

  const errorElement = await screen.findByText(/Сервер недоступен/i);
  expect(errorElement).toBeInTheDocument();
});

Рекомендации по асинхронным тестам:

  • findBy* автоматически ждёт появления элемента до заданного таймаута.
  • waitFor используется для проверки изменений состояния, которые не сразу отражаются в DOM.

Организация моков для повторного использования

В больших проектах рекомендуется создавать утилиты для мокирования запросов. Пример:

// test-utils/axiosMock.js
import axios from 'axios';

export const mockAxiosGet = (url, response) => {
  axios.get.mockImplementation((requestUrl) => {
    if (requestUrl === url) {
      return Promise.resolve({ data: response });
    }
    return Promise.reject(new Error('Неизвестный URL'));
  });
};

Использование в тесте:

import { mockAxiosGet } from './test-utils/axiosMock';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('использует утилиту мокирования', async () => {
  mockAxiosGet('/api/message', { message: 'Привет!' });

  render(<MyComponent />);
  const element = await screen.findByText(/Привет!/i);
  expect(element).toBeInTheDocument();
});

Такой подход делает тесты более читаемыми и уменьшает дублирование кода.

Частые ошибки при мокировании axios

  • Отсутствие await или использования findBy* при проверке асинхронного контента. В результате тест проходит, но компонент не успевает обновить DOM.

  • Неправильное использование mockResolvedValue вместо mockResolvedValueOnce. Если тестов несколько, глобальный мок может влиять на другие тесты.

  • Зависимость от внешних данных. Мокирование необходимо для всех запросов, чтобы тесты были детерминированными.

  • Неправильное восстановление состояния мока между тестами. Рекомендуется использовать beforeEach(() => jest.clearAllMocks()) для очистки моков.

Мокирование axios в React Testing Library позволяет полностью контролировать поведение компонентов при различных сценариях взаимодействия с API, ускоряет тесты и повышает их стабильность. Правильная организация моков и использование асинхронных утилит RTL обеспечивают надёжность и читаемость тестового кода.