Моки для модулей и импортов

При тестировании Vue-компонентов часто возникает необходимость изолировать их от внешних зависимостей: сервисов, утилит, библиотек или других компонентов. Для этого используются моки модулей и импортов, что позволяет контролировать поведение зависимостей и проверять реакцию компонента на различные сценарии.

Основные принципы мокирования

Мокирование заключается в замене реальной реализации модуля на имитацию, которая возвращает предсказуемые значения. Это дает несколько преимуществ:

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

В Vue Test Utils мокирование тесно связано с возможностями тестового раннера (например, Jest или Vitest), так как именно он предоставляет инструменты для подмены импортов.

Моки модулей с Jest

Jest предоставляет функцию jest.mock(), позволяющую заменить любой импортируемый модуль:

import { mount } from '@vue/test-utils';
import UserService from '@/services/UserService';
import UserProfile from '@/components/UserProfile.vue';

jest.mock('@/services/UserService', () => ({
  fetchUser: jest.fn(() => Promise.resolve({ id: 1, name: 'Иван' })),
}));

test('должен отображать имя пользователя', async () => {
  const wrapper = mount(UserProfile);
  await wrapper.vm.$nextTick();
  expect(wrapper.text()).toContain('Иван');
});

Здесь fetchUser заменен на мок-функцию, которая всегда возвращает один и тот же объект. Это позволяет тестировать компонент без обращения к реальному сервису.

Моки с Vitest

Vitest использует схожий синтаксис с Jest, но с небольшой особенностью для модулей ES:

import { vi } from 'vitest';
import { mount } from '@vue/test-utils';
import UserService from '@/services/UserService';
import UserProfile from '@/components/UserProfile.vue';

vi.mock('@/services/UserService', () => ({
  fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: 'Мария' })),
}));

test('отображает имя пользователя', async () => {
  const wrapper = mount(UserProfile);
  await wrapper.vm.$nextTick();
  expect(wrapper.text()).toContain('Мария');
});

При использовании Vitest важно помнить, что мок должен быть объявлен до импорта тестируемого компонента, иначе реальный модуль уже будет подгружен.

Мокирование отдельных функций

Иногда необходимо заменить не весь модуль, а только отдельные функции. В Jest это делается через jest.spyOn:

import * as utils from '@/utils/formatDate';
import { mount } from '@vue/test-utils';
import DateDisplay from '@/components/DateDisplay.vue';

jest.spyOn(utils, 'formatDate').mockReturnValue('01.01.2026');

test('отображает отформатированную дату', () => {
  const wrapper = mount(DateDisplay, {
    props: { date: '2026-01-01T00:00:00Z' },
  });
  expect(wrapper.text()).toContain('01.01.2026');
});

spyOn позволяет временно переопределять функцию без изменения остального модуля и легко сбрасывается после теста.

Автоматические моки

Для сложных проектов часто используют автоматическое мокирование модулей. В Jest это достигается вызовом jest.mock('moduleName') без второго аргумента. В этом случае Jest создает мок-объект с функциями-заглушками.

jest.mock('@/services/UserService');

import UserService from '@/services/UserService';

UserService.fetchUser.mockResolvedValue({ id: 2, name: 'Алексей' });

Автомоки удобны для контроля всех функций модуля и для тестирования ошибок без необходимости писать собственные реализации.

Важно при мокировании

  1. Последовательность импортов. Моки должны объявляться до того, как модуль или компонент будет импортирован.
  2. Очистка моков. После теста рекомендуется сбрасывать состояния моков через jest.resetAllMocks() или vi.clearAllMocks().
  3. Тестирование реакций на ошибки. Моки позволяют легко симулировать исключения:
jest.mock('@/services/UserService', () => ({
  fetchUser: jest.fn(() => Promise.reject(new Error('Сервис недоступен'))),
}));

Компонент при этом можно проверять на правильное отображение сообщений об ошибках.

Моки компонентов

Помимо сервисов, Vue Test Utils позволяет мокировать дочерние компоненты:

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

const wrapper = mount(ParentComponent, {
  global: {
    stubs: {
      ChildComponent: {
        template: '<div class="mock-child">Мок компонент</div>',
      },
    },
  },
});

expect(wrapper.html()).toContain('Мок компонент');

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

Комбинирование моков

На практике часто требуется одновременно мокировать сервисы и компоненты:

import { mount } from '@vue/test-utils';
import { vi } from 'vitest';
import Parent from '@/components/Parent.vue';
import ApiService from '@/services/ApiService';

vi.mock('@/services/ApiService', () => ({
  fetchData: vi.fn(() => Promise.resolve(['item1', 'item2'])),
}));

const wrapper = mount(Parent, {
  global: {
    stubs: {
      Child: true,
    },
  },
});

await wrapper.vm.$nextTick();
expect(wrapper.text()).toContain('item1');

Такой подход делает тесты более предсказуемыми и управляемыми.

Заключение принципов

Моки модулей и импортов — ключевой инструмент для изолированного тестирования Vue-компонентов. Они позволяют полностью контролировать внешние зависимости, симулировать различные сценарии, ошибки и исключения, а также ускорять тестирование за счет исключения сетевых запросов и тяжелых вычислений. Использование jest.mock, vi.mock и jest.spyOn обеспечивает гибкость при замене функций или целых модулей, а глобальные стабы компонентов делают тесты компактными и устойчивыми.