Мокирование зависимостей

Мокирование зависимостей является ключевым инструментом при тестировании приложений на Vue.js. Оно позволяет изолировать компонент или сервис от внешних зависимостей, таких как API-запросы, сторонние библиотеки или глобальные состояния, что обеспечивает более точное и управляемое тестирование.

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

В Vue.js компоненты часто зависят от сервисов, Vuex store, router и внешних API. Мокирование этих зависимостей позволяет тестировать компоненты как юнит-тесты, избегая непредсказуемого поведения из-за внешних факторов.

Пример структуры компонента с зависимостью:

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="fetchData">Загрузить данные</button>
  </div>
</template>

<script>
import apiService from '@/services/apiService';

export default {
  data() {
    return {
      message: ''
    };
  },
  methods: {
    async fetchData() {
      const result = await apiService.getData();
      this.message = result.message;
    }
  }
};
</script>

В этом примере apiService является внешней зависимостью, которую можно замокировать для тестирования метода fetchData.

Мокирование с Jest

Jest предоставляет встроенные инструменты для создания моков. Основные методы: jest.mock() и jest.fn().

Пример мокирования сервиса:

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

jest.mock('@/services/apiService', () => ({
  getData: jest.fn()
}));

describe('MyComponent', () => {
  it('должен обновлять message после fetchData', async () => {
    apiService.getData.mockResolvedValue({ message: 'Привет, Vue!' });

    const wrapper = shallowMount(MyComponent);
    await wrapper.vm.fetchData();

    expect(wrapper.vm.message).toBe('Привет, Vue!');
  });
});

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

  • jest.mock() заменяет весь модуль на объект с моками.
  • mockResolvedValue() позволяет задать возвращаемое значение для промиса.
  • Тест изолирует компонент от реального API, что ускоряет выполнение и повышает стабильность.

Мокирование Vuex

Компоненты часто используют mapState, mapActions или напрямую обращаются к store. Для тестирования таких компонентов создаётся локальный store или моки для отдельных действий.

import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import Counter from '@/components/Counter.vue';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('Counter', () => {
  let actions;
  let store;

  beforeEach(() => {
    actions = {
      increment: jest.fn()
    };
    store = new Vuex.Store({
      state: { count: 0 },
      actions
    });
  });

  it('вызывает действие increment', () => {
    const wrapper = shallowMount(Counter, { store, localVue });
    wrapper.find('button').trigger('click');
    expect(actions.increment).toHaveBeenCalled();
  });
});

Особенности мокирования Vuex:

  • Использование createLocalVue() предотвращает изменения глобального Vue.
  • Моки действий (actions) позволяют проверять вызовы без выполнения реальной логики.
  • Можно замокировать состояние (state) для проверки реактивности.

Мокирование роутера

Vue Router также часто является зависимостью. Для тестов создаются локальные экземпляры маршрутизатора с необходимыми маршрутами.

import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueRouter from 'vue-router';
import MyPage from '@/components/MyPage.vue';

const localVue = createLocalVue();
localVue.use(VueRouter);

const routes = [{ path: '/home', name: 'home' }];
const router = new VueRouter({ routes });

describe('MyPage', () => {
  it('редиректит на home', async () => {
    const wrapper = shallowMount(MyPage, { localVue, router });
    await wrapper.vm.$router.push('/home');
    expect(wrapper.vm.$route.name).toBe('home');
  });
});

Преимущества мокирования роутера:

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

Мокирование сторонних библиотек

В проектах на Vue.js часто используются библиотеки для HTTP-запросов (Axios), уведомлений (Toast), графиков (Chart.js). Их также можно мокировать:

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

axios.get.mockResolvedValue({ data: { id: 1, name: 'Vue' } });

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

  • Мокирование сторонних библиотек предотвращает сетевые вызовы.
  • Позволяет тестировать обработку ошибок и успешные ответы.
  • jest.fn() можно использовать для проверки вызовов функций и аргументов.

Рекомендации по мокированию

  • Мокировать только непосредственные зависимости, а не внутреннюю логику компонента.
  • Для сложных сервисов использовать фабрики моков, чтобы легко менять их поведение в разных тестах.
  • Всегда очищать моки между тестами с помощью jest.clearAllMocks() или jest.resetAllMocks().
  • Для реактивных данных Vuex или props можно использовать shallowMount вместо mount, чтобы изолировать компонент от дочерних компонентов.

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