Перехват и моки HTTP запросов

При тестировании компонентов Vue часто возникает необходимость проверять поведение приложения при взаимодействии с внешними API. Реальные HTTP-запросы замедляют тесты и делают их нестабильными, поэтому используется подход с перехватом запросов и созданием моков.


Использование мока axios

Чаще всего в проектах Vue HTTP-запросы выполняются через axios. Для тестирования удобно подменять его поведение.

Пример настройки мока с Jest:

import { shallowMount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
import axios from 'axios'

jest.mock('axios')

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

  const wrapper = shallowMount(MyComponent)
  await wrapper.vm.fetchData() // метод компонента, который вызывает axios.get

  expect(wrapper.vm.data).toEqual(mockData.data)
  expect(axios.get).toHaveBeenCalledWith('/api/data')
})

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

  • jest.mock('axios') подменяет модуль axios во всех тестах.
  • mockResolvedValue используется для имитации успешного ответа.
  • Проверка toHaveBeenCalledWith позволяет убедиться, что запрос отправлен по правильному URL.

Перехват fetch

Если проект использует fetch, его также можно замокать:

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ name: 'Vue' })
  })
)

test('fetch-запрос возвращает данные', async () => {
  const wrapper = shallowMount(MyComponent)
  await wrapper.vm.loadData()

  expect(wrapper.vm.data.name).toBe('Vue')
  expect(fetch).toHaveBeenCalledWith('/api/data')
})

Особенности:

  • global.fetch = jest.fn() подменяет стандартный fetch.
  • Методы json, text и другие можно настраивать для имитации различных ответов.
  • После тестов рекомендуется очищать мок: fetch.mockClear().

Использование библиотеки msw для более сложных сценариев

msw (Mock Service Worker) позволяет перехватывать HTTP-запросы на уровне сетевого слоя, что удобно для интеграционных тестов:

import { setupServer } from 'msw/node'
import { rest } from 'msw'
import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'

const server = setupServer(
  rest.get('/api/data', (req, res, ctx) => {
    return res(ctx.json({ name: 'Vue' }))
  })
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('компонент получает данные через msw', async () => {
  const wrapper = mount(MyComponent)
  await wrapper.vm.fetchData()

  expect(wrapper.vm.data.name).toBe('Vue')
})

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

  • Можно легко тестировать ошибки сервера и разные коды ответа.
  • Подходит как для unit, так и для интеграционных тестов.
  • Реалистично эмулирует сетевой уровень без обращения к реальному API.

Проверка обработки ошибок

Тестирование компонентов должно включать сценарии с ошибками:

axios.get.mockRejectedValue(new Error('Network Error'))

test('компонент обрабатывает ошибку запроса', async () => {
  const wrapper = shallowMount(MyComponent)
  await wrapper.vm.fetchData()

  expect(wrapper.vm.error).toBe('Network Error')
})

Важно:

  • Обязательно проверять состояние компонента при ошибках (error, loading).
  • Подобные тесты предотвращают скрытые баги в обработке API.

Комбинация Vue Test Utils и таймеров

Для асинхронных запросов часто требуется управление таймерами:

jest.useFakeTimers()

test('данные загружаются после задержки', async () => {
  axios.get.mockImplementation(() =>
    new Promise(resolve => setTimeout(() => resolve({ data: { name: 'Vue' } }), 1000))
  )

  const wrapper = shallowMount(MyComponent)
  const promise = wrapper.vm.fetchData()

  jest.advanceTimersByTime(1000)
  await promise

  expect(wrapper.vm.data.name).toBe('Vue')
})

Пояснение:

  • jest.useFakeTimers() позволяет контролировать setTimeout и setInterval.
  • jest.advanceTimersByTime ускоряет прохождение асинхронных операций.
  • Такой подход снижает время выполнения тестов и делает их детерминированными.

Подходы к мокам в Vue Test Utils

  1. Локальные моки компонентов: Подменяются только методы компонента, не внешние зависимости.
  2. Моки внешних модулей: Через Jest (jest.mock) или sinon.
  3. Интеграционные моки: Через msw или аналогичные сервисы для перехвата реальных HTTP-запросов.

Каждый подход имеет свои сильные стороны: первые удобны для unit-тестов, вторые — для проверки взаимодействия с API без сетевых вызовов, третьи — для интеграционного тестирования в условиях максимально приближенных к реальности.


Практические рекомендации

  • Всегда очищать моки между тестами: mockClear или resetHandlers.
  • Разделять тесты на успешные и ошибочные сценарии.
  • Для крупных компонентов использовать mount вместо shallowMount, чтобы проверить реактивность и DOM.
  • Для сложных цепочек запросов применять msw, чтобы избежать громоздких моков вручную.

Перехват и моки HTTP-запросов — ключевой инструмент для надёжного тестирования Vue-компонентов, позволяющий создавать стабильные, быстрые и предсказуемые тесты без обращения к реальным API.