Использование MSW (Mock Service Worker)

MSW (Mock Service Worker) представляет собой инструмент для перехвата HTTP-запросов на уровне браузера или Node.js и подмены их ответов. В сочетании с Vue Test Utils он позволяет изолированно тестировать компоненты, взаимодействующие с API, без необходимости настоящих сетевых вызовов. Это обеспечивает стабильность тестов, предсказуемость результатов и возможность моделировать различные состояния сервера.


Установка и настройка MSW

Для работы с MSW необходимо установить пакет через npm:

npm install msw --save-dev

Создание обработчиков запросов (handlers) осуществляется с помощью функции rest:

import { rest } from 'msw';

export const handlers = [
  rest.get('/api/users', (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json([
        { id: 1, name: 'Иван' },
        { id: 2, name: 'Мария' },
      ])
    );
  }),
  rest.post('/api/users', (req, res, ctx) => {
    const { name } = req.body;
    return res(
      ctx.status(201),
      ctx.json({ id: Math.floor(Math.random() * 1000), name })
    );
  }),
];

Для браузерного окружения используется сервис-воркер:

import { setupWorker } from 'msw';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);
worker.start();

В Node.js среде (например, при запуске Jest) применяется setupServer:

import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// Настройка Jest хуков
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Интеграция MSW с Vue Test Utils

Vue Test Utils предоставляет методы для монтирования компонентов и симуляции взаимодействия пользователя. При этом любые сетевые запросы, инициируемые компонентом, будут перехвачены MSW.

Пример теста компонента UserList.vue:

import { mount } from '@vue/test-utils';
import UserList from '@/components/UserList.vue';
import { server } from '@/mocks/server';
import { rest } from 'msw';

describe('UserList', () => {
  it('отображает список пользователей', async () => {
    const wrapper = mount(UserList);

    // Ждем завершения асинхронного обновления компонента
    await wrapper.vm.$nextTick();

    const items = wrapper.findAll('li');
    expect(items).toHaveLength(2);
    expect(items[0].text()).toBe('Иван');
    expect(items[1].text()).toBe('Мария');
  });

  it('обрабатывает ошибку сервера', async () => {
    server.use(
      rest.get('/api/users', (req, res, ctx) => {
        return res(ctx.status(500));
      })
    );

    const wrapper = mount(UserList);
    await wrapper.vm.$nextTick();

    expect(wrapper.text()).toContain('Ошибка загрузки пользователей');
  });
});

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

  • server.use позволяет временно переопределить обработчики для конкретного теста.
  • Асинхронные обновления компонента отслеживаются через await wrapper.vm.$nextTick() или flushPromises.
  • Использование MSW гарантирует, что тесты не зависят от состояния внешнего API.

Работа с асинхронными данными

Vue-компоненты часто используют fetch или axios для получения данных. MSW полностью поддерживает оба подхода.

Пример с axios:

import axios from 'axios';

export async function fetchUsers() {
  const response = await axios.get('/api/users');
  return response.data;
}

Тестирование:

import { fetchUsers } from '@/api/users';

it('возвращает список пользователей через axios', async () => {
  const users = await fetchUsers();
  expect(users).toEqual([
    { id: 1, name: 'Иван' },
    { id: 2, name: 'Мария' },
  ]);
});

MSW перехватывает axios-запросы точно так же, как обычный fetch, обеспечивая универсальность.


Моделирование различных состояний сервера

MSW позволяет эмулировать:

  • Успешные ответы (ctx.status(200)),
  • Ошибки сервера (ctx.status(500)),
  • Задержки ответа (ctx.delay(1000)),
  • Пустые данные или частичные наборы данных.

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

server.use(
  rest.get('/api/users', (req, res, ctx) => {
    return res(
      ctx.delay(500),
      ctx.status(200),
      ctx.json([{ id: 1, name: 'Иван' }])
    );
  })
);

Такой подход позволяет проверять состояние загрузки (loading) в компонентах и корректное отображение индикаторов прогресса.


Комбинация Vue Test Utils и MSW для комплексных сценариев

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

server.use(
  rest.get('/api/users', (req, res, ctx) => res(ctx.json([{ id: 1, name: 'Иван' }]))),
  rest.get('/api/posts', (req, res, ctx) => res(ctx.json([{ id: 101, title: 'Пост 1' }])))
);

Компонент может одновременно отображать пользователей и посты. Использование MSW позволяет эмулировать сценарии с разными комбинациями ответов API, включая ошибки или частичные данные.


Логирование и отладка

MSW поддерживает логирование запросов и ответов, что облегчает отладку тестов:

import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

server.events.on('request:start', (req) => {
  console.log('Запрос:', req.url.href);
});

server.events.on('response:mocked', (res, req) => {
  console.log('Мок-ответ для', req.url.href, 'Статус:', res.status);
});

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


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

  • Все сетевые запросы в тестах следует мокировать, чтобы избежать зависимости от внешних сервисов.
  • Для динамических тестов использовать server.use с переопределением обработчиков на уровне конкретного теста.
  • Асинхронные состояния компонентов проверять через flushPromises или nextTick.
  • Отдельно тестировать обработку ошибок и пустые ответы.
  • Логи MSW полезны для диагностики нестабильных тестов.

Использование MSW вместе с Vue Test Utils делает тестирование компонентов, взаимодействующих с API, надежным и предсказуемым, упрощает управление асинхронностью и моделирование различных сценариев сервера.