jest.mock() и jest.spyOn()

В экосистеме Vue Test Utils тесно интегрируется Jest, предоставляющий мощные средства для создания заглушек и слежения за функциями. Основные инструменты — jest.mock() и jest.spyOn() — позволяют контролировать поведение зависимостей и проверять вызовы функций, что критически важно при модульном тестировании.


jest.mock(): создание заглушек модулей

jest.mock() позволяет полностью подменить импортируемый модуль, заменяя его фиктивной реализацией. Это особенно полезно при тестировании компонентов, которые зависят от внешних библиотек или сервисов, например, API-запросов.

Синтаксис базового использования:

jest.mock('axios');
import axios from 'axios';

axios.get.mockResolvedValue({ data: { message: 'OK' } });

Применение в Vue-компонентах:

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

jest.mock('axios');

test('должен отображать сообщение от API', async () => {
  axios.get.mockResolvedValue({ data: { message: 'Привет' } });
  
  const wrapper = mount(MyComponent);
  await wrapper.vm.fetchMessage();
  
  expect(wrapper.text()).toContain('Привет');
});

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

  • Все вызовы методов модуля будут перенаправлены на заглушку.
  • Можно управлять поведением отдельных методов через mockResolvedValue, mockRejectedValue, mockImplementation.
  • Позволяет тестировать компонент в изоляции, не делая реальных сетевых запросов.

jest.spyOn(): слежение за функциями

jest.spyOn() используется для наблюдения за существующими методами объектов или классов без полной подмены модуля. Это подходит для проверки вызовов методов жизненного цикла компонентов, методов сервисов или внутренних функций.

Пример слежения за методом:

const api = {
  fetchData: () => Promise.resolve('данные')
};

test('вызывает fetchData один раз', async () => {
  const spy = jest.spyOn(api, 'fetchData');
  
  await api.fetchData();
  
  expect(spy).toHaveBeenCalledTimes(1);
});

Использование в Vue Test Utils:

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

test('метод getUser вызывается при монтировании', async () => {
  const spy = jest.spyOn(userService, 'getUser').mockResolvedValue({ name: 'Иван' });
  
  const wrapper = mount(UserProfile);
  await wrapper.vm.$nextTick();
  
  expect(spy).toHaveBeenCalled();
  expect(wrapper.text()).toContain('Иван');
  
  spy.mockRestore();
});

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

  • jest.spyOn() возвращает объект шпиона, который можно использовать для проверки количества вызовов, аргументов и возвращаемых значений.
  • С помощью mockImplementation можно изменить поведение конкретного вызова.
  • После теста рекомендуется использовать mockRestore() для восстановления оригинального метода, чтобы не повлиять на последующие тесты.

Различия между jest.mock() и jest.spyOn()

Характеристика jest.mock() jest.spyOn()
Подмена модуля Полная Частичная, только метод
Контроль над реализацией Да, через mockImplementation Да, через mockImplementation
Влияние на другие тесты Может требовать ручного сброса Требует вызова mockRestore()
Применение Внешние зависимости, API, библиотеки Методы сервисов, внутренние функции компонента

Советы по применению в тестировании Vue

  1. Изоляция компонентов: для модулей, выполняющих сетевые запросы или обращение к базе, использовать jest.mock().
  2. Проверка поведения методов: если нужно проверить, что компонент вызвал определённую функцию при событии или монтировании, применять jest.spyOn().
  3. Комбинированное использование: часто jest.mock() создаёт базовую заглушку, а jest.spyOn() добавляет слежение за отдельными методами.
  4. Управление асинхронностью: при работе с промисами необходимо использовать await wrapper.vm.$nextTick() или await flushPromises(), чтобы тест корректно дождался завершения асинхронной логики.
  5. Очистка после тестов: jest.clearAllMocks() и jest.restoreAllMocks() предотвращают «загрязнение» глобального состояния между тестами.

Примеры сложных сценариев

Комбинация mock и spy для API-сервисов:

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

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

test('отображает список пользователей после вызова API', async () => {
  const spy = jest.spyOn(apiService, 'getUsers').mockResolvedValue([{ id: 1, name: 'Алекс' }]);
  
  const wrapper = mount(Dashboard);
  await wrapper.vm.$nextTick();
  
  expect(spy).toHaveBeenCalled();
  expect(wrapper.text()).toContain('Алекс');
  
  spy.mockRestore();
});

Перехват нескольких методов одного модуля:

import utils from '@/utils/math';

jest.mock('@/utils/math', () => ({
  add: jest.fn(),
  subtract: jest.fn(),
}));

utils.add.mockImplementation((a, b) => a + b);
utils.subtract.mockImplementation((a, b) => a - b);

test('тестирование математических операций', () => {
  expect(utils.add(2, 3)).toBe(5);
  expect(utils.subtract(5, 2)).toBe(3);
});

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