Тестирование компонентов с fetch/axios

Тестирование компонентов, которые выполняют асинхронные HTTP-запросы, требует особого подхода. В Vue-приложениях чаще всего используются fetch или axios для получения данных с сервера. Важная цель — проверить поведение компонента без реального сетевого запроса, имитируя ответы сервера и ошибки. Это обеспечивает стабильность и скорость тестов.


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

Для тестирования с fetch и axios необходимо создавать моки — имитации функций, возвращающих ожидаемые данные.

Мок fetch

fetch — глобальная функция браузера. Для её мокирования часто используется Jest:

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

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

Пример теста с успешным ответом:

import { mount } from '@vue/test-utils';
import DataComponent from '@/components/DataComponent.vue';

test('должен отображать данные после fetch', async () => {
  const mockData = { name: 'Vue' };
  global.fetch.mockResolvedValue({
    ok: true,
    json: async () => mockData,
  });

  const wrapper = mount(DataComponent);
  
  await wrapper.vm.$nextTick(); // ожидание обновления DOM
  await wrapper.vm.$nextTick(); // может потребоваться повторный тик для асинхронного метода

  expect(wrapper.text()).toContain('Vue');
});

Если требуется протестировать ошибку запроса:

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

const wrapper = mount(DataComponent);

await wrapper.vm.$nextTick();

expect(wrapper.text()).toContain('Ошибка загрузки данных');

Мок axios

axios легче мокировать с помощью jest.mock:

import axios from 'axios';
import { mount } from '@vue/test-utils';
import DataComponent from '@/components/DataComponent.vue';

jest.mock('axios');

test('должен загружать данные через axios', async () => {
  const mockData = { data: { name: 'Vue' } };
  axios.get.mockResolvedValue(mockData);

  const wrapper = mount(DataComponent);

  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('Vue');
});

test('обработка ошибки axios', async () => {
  axios.get.mockRejectedValue(new Error('Ошибка сервера'));

  const wrapper = mount(DataComponent);

  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('Ошибка загрузки данных');
});

Асинхронные хуки и жизненный цикл компонента

Компоненты, использующие fetch или axios, чаще всего выполняют запросы в mounted или created. Для корректного тестирования нужно:

  1. Мокировать HTTP-запрос до монтирования компонента.
  2. Использовать await wrapper.vm.$nextTick() или await flushPromises() для ожидания завершения промисов.

Пример с flush-promises:

import flushPromises from 'flush-promises';

const wrapper = mount(DataComponent);
await flushPromises(); // дожидается всех промисов
expect(wrapper.text()).toContain('Vue');

flush-promises удобен, когда компонент выполняет несколько последовательных асинхронных операций.


Проверка состояний загрузки и ошибок

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

test('показывает индикатор загрузки', async () => {
  global.fetch.mockImplementation(() => new Promise(() => {})); // имитация долгого запроса

  const wrapper = mount(DataComponent);

  expect(wrapper.text()).toContain('Загрузка...');
});

Для ошибок:

global.fetch.mockRejectedValue(new Error('Fail'));

const wrapper = mount(DataComponent);
await flushPromises();

expect(wrapper.text()).toContain('Ошибка загрузки данных');

Использование spy для проверок вызовов

Помимо мокирования, можно использовать jest.spyOn для отслеживания вызовов методов:

import * as api from '@/api/data';

jest.spyOn(api, 'fetchData').mockResolvedValue({ name: 'Vue' });

const wrapper = mount(DataComponent);
await flushPromises();

expect(api.fetchData).toHaveBeenCalledTimes(1);
expect(wrapper.text()).toContain('Vue');

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


Интеграция с Vuex и props

Если компонент получает URL через props или dispatch в Vuex:

jest.mock('axios');

const wrapper = mount(DataComponent, {
  props: { url: '/api/data' },
  global: {
    mocks: {
      $store: {
        dispatch: jest.fn().mockResolvedValue({ name: 'Vue' })
      }
    }
  }
});

await flushPromises();

expect(wrapper.text()).toContain('Vue');

Таким образом, тест учитывает передаваемые параметры и состояние хранилища, оставаясь изолированным от реального API.


Рекомендации по стабильности тестов

  • Всегда сбрасывать моки после каждого теста (jest.resetAllMocks()).
  • Использовать flushPromises для ожидания всех асинхронных операций.
  • Проверять как успешные, так и ошибочные ответы.
  • Избегать настоящих сетевых запросов, чтобы тесты были быстрыми и детерминированными.
  • Разделять тесты по сценариям: загрузка, успех, ошибка, пустой ответ.

Тщательное мокирование запросов и контроль асинхронного поведения обеспечивает надежное тестирование компонентов Vue, использующих fetch или axios.