Проверка количества срабатываний события

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

Основные инструменты

Vue Test Utils предоставляет несколько ключевых средств для работы с событиями:

  • emitted(): возвращает объект, содержащий все события, сгенерированные компонентом, сгруппированные по имени события.
  • wrapper.trigger(): позволяет программно вызвать событие DOM, например, клик.
  • Jest-совместимые функции toHaveBeenCalledTimes() и mockImplementation() для проверки вызовов методов и функций.

Пример базовой проверки

Предположим, есть компонент CounterButton.vue:

<template>
  <button @click="increment">Нажми меня</button>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
      this.$emit('increment', this.count)
    }
  }
}
</script>

Для проверки количества срабатываний события increment можно использовать следующий подход:

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

test('событие increment срабатывает нужное количество раз', async () => {
  const wrapper = mount(CounterButton)

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

  const emittedEvents = wrapper.emitted('increment')
  expect(emittedEvents).toHaveLength(3)
})

Ключевой момент: метод emitted('eventName') возвращает массив, где каждый элемент — массив аргументов события. Использование toHaveLength() позволяет проверить точное число срабатываний.

Проверка с конкретными аргументами

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

expect(emittedEvents).toEqual([[1], [2], [3]])

Здесь проверяется, что первый клик передал 1, второй — 2, третий — 3. Такой подход позволяет обнаруживать ошибки инкрементации и неправильной логики обработки событий.

Использование Jest-Spy для проверки внутренних методов

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

test('метод increment вызывается 3 раза при кликах', async () => {
  const wrapper = mount(CounterButton)
  const spy = jest.spyOn(wrapper.vm, 'increment')

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

  expect(spy).toHaveBeenCalledTimes(3)
})

Сложные сценарии: асинхронные события

Если событие срабатывает после асинхронной операции (например, API-запроса или setTimeout), необходимо использовать async/await и nextTick:

<template>
  <button @click="delayedEmit">Отложенный клик</button>
</template>

<script>
export default {
  methods: {
    async delayedEmit() {
      await new Promise(resolve => setTimeout(resolve, 50))
      this.$emit('delayed', 1)
    }
  }
}
</script>

Тест:

import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import DelayedButton from '@/components/DelayedButton.vue'

test('событие delayed срабатывает 2 раза', async () => {
  const wrapper = mount(DelayedButton)

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

  await new Promise(resolve => setTimeout(resolve, 60))
  await nextTick()

  expect(wrapper.emitted('delayed')).toHaveLength(2)
})

Особенность: без await nextTick() массив событий может быть пустым, так как Vue ещё не завершил обновление виртуального DOM.

Советы по структурированию тестов событий

  1. Явное указание количества: проверка toHaveLength() лучше, чем проверка > 0, так как исключает скрытые баги.
  2. Разделение тестов: лучше тестировать количество срабатываний и аргументы события отдельно для повышения читаемости.
  3. Spy vs emitted: использование шпионов оправдано, когда важно отслеживать вызовы методов, а emitted() — для внешнего поведения компонента.

Вывод

Контроль количества срабатываний событий в Vue Test Utils позволяет выявлять баги, связанные с избыточной генерацией событий или неправильной логикой. Комбинация методов emitted(), trigger() и Jest-спаев обеспечивает полное покрытие проверок, включая асинхронные сценарии и проверку переданных аргументов. Такой подход гарантирует надежное тестирование компонентов с интерактивным поведением.