Проверка действий (actions)

В тестировании Vue-компонентов ключевое место занимает проверка actions — функций, вызываемых при взаимодействии с компонентом, таких как клик по кнопке, изменение данных формы или вызов методов Vuex. Правильное тестирование действий позволяет убедиться, что компонент корректно реагирует на события и взаимодействует с состоянием приложения.


Подготовка тестового окружения

Для начала необходимо настроить тестовое окружение с использованием Vue Test Utils и тестового раннера, например, Jest. Основные импорты:

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

Для компонентов, использующих Vuex, необходимо подключение мок-стора:

import Vuex from 'vuex';

const actions = {
  myAction: jest.fn()
};

const store = new Vuex.Store({
  actions
});

Здесь jest.fn() создаёт имитацию функции, которая позволяет отслеживать вызовы действия.


Тестирование событий и вызовов действий

1. Проверка вызова метода компонента при событии

Методы компонентов можно проверять через wrapper.vm или через вызов события. Например, метод submitForm должен вызываться при сабмите формы:

const wrapper = mount(Component);

const form = wrapper.find('form');
form.trigger('submit.prevent');

expect(wrapper.vm.submitForm).toHaveBeenCalled();

Ключевой момент: .trigger('event') симулирует событие, а .toHaveBeenCalled() проверяет, что метод был вызван.


2. Проверка Vuex actions

Если компонент диспатчит action Vuex, используется мок-store. Например, компонент вызывает myAction при клике на кнопку:

const wrapper = mount(Component, { global: { plugins: [store] } });

const button = wrapper.find('button');
button.trigger('click');

expect(actions.myAction).toHaveBeenCalled();

Можно также проверить, что action был вызван с правильными аргументами:

expect(actions.myAction).toHaveBeenCalledWith(expect.anything(), { id: 123 });

expect.anything() используется для аргумента context, который Vuex передает автоматически.


Работа с асинхронными действиями

Многие actions являются асинхронными. Для их тестирования важно использовать async/await и методы flushPromises:

import flushPromises from 'flush-promises';

actions.asyncAction = jest.fn(() => Promise.resolve('done'));

const wrapper = mount(Component, { global: { plugins: [store] } });

wrapper.find('button').trigger('click');

await flushPromises();

expect(actions.asyncAction).toHaveBeenCalled();

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


Проверка параметров и количества вызовов

Важно контролировать частоту вызова action и передаваемые данные:

expect(actions.myAction).toHaveBeenCalledTimes(1);
expect(actions.myAction).toHaveBeenCalledWith(expect.anything(), { name: 'John' });
  • toHaveBeenCalledTimes(n) — проверяет количество вызовов.
  • toHaveBeenCalledWith(...) — проверяет точные аргументы.

Эти проверки особенно важны для сложных форм и компонентов с динамическими действиями.


Использование spies для локальных методов

Иногда нужно проверить внутренние методы компонента, не только Vuex actions. В этом случае используется jest.spyOn:

const spy = jest.spyOn(Component.methods, 'localMethod');

const wrapper = mount(Component);
wrapper.find('button').trigger('click');

expect(spy).toHaveBeenCalled();
spy.mockRestore();

mockRestore восстанавливает исходную функцию, что предотвращает побочные эффекты в других тестах.


Советы по организации тестов действий

  1. Разделять тесты по сценариям: клики, сабмиты, ввод данных.
  2. Использовать отдельные моки для Vuex и компонентов с тяжелой логикой.
  3. Проверять как синхронные, так и асинхронные вызовы.
  4. Минимизировать взаимодействие с реальным DOM, используя shallow mount, когда это возможно.

Практика с параметризированными тестами

Для проверки множества действий с разными аргументами удобно использовать test.each:

test.each([
  ['John', 1],
  ['Jane', 2]
])('диспатч action с %s и %i', async (name, id) => {
  const wrapper = mount(Component, { global: { plugins: [store] } });
  wrapper.find('button').trigger('click');
  await flushPromises();
  expect(actions.myAction).toHaveBeenCalledWith(expect.anything(), { name, id });
});

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


Проверка последовательности действий

Если компонент вызывает несколько действий в определённой последовательности, важно проверять порядок вызовов:

const firstAction = jest.fn();
const secondAction = jest.fn();

const store = new Vuex.Store({ actions: { firstAction, secondAction } });

const wrapper = mount(Component, { global: { plugins: [store] } });
wrapper.find('button').trigger('click');

expect(firstAction).toHaveBeenCalledBefore(secondAction);

Для этого можно использовать сторонние Jest-плагины, такие как jest-extended, которые расширяют стандартные матчер-функции.


Выводы

Тестирование действий во Vue Test Utils — это проверка корректной реакции компонентов на события и правильного взаимодействия с Vuex. Основные принципы включают:

  • Использование моков для действий Vuex.
  • Симуляцию событий через .trigger().
  • Проверку вызовов, аргументов и количества вызовов.
  • Управление асинхронностью через async/await и flushPromises.
  • Организацию тестов для масштабируемости и читаемости.

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