Проверка update:modelValue события

update:modelValue является стандартным событием во Vue 3, используемым в компонентах с двусторонней привязкой через директиву v-model. Тестирование таких событий важно для проверки корректного взаимодействия компонентов и родительских элементов. Vue Test Utils предоставляет удобные инструменты для перехвата и проверки эмитированных событий.

Основы эмитации update:modelValue

В Vue 3 синтаксис двусторонней привязки выглядит так:

<MyInput v-model="textValue" />

Внутри компонента MyInput изменение значения происходит через эмит события:

this.$emit('update:modelValue', newValue)

Для тестирования необходимо проверить, что компонент корректно эмитирует событие при взаимодействии пользователя.

Создание теста с Vue Test Utils

Импортируем необходимые функции:

import { mount } from '@vue/test-utils'
import MyInput from '@/components/MyInput.vue'
Пример базового теста
test('эмитирует update:modelValue при вводе текста', async () => {
  const wrapper = mount(MyInput)

  // Симулируем пользовательский ввод
  const input = wrapper.find('input')
  await input.setValue('Hello Vue')

  // Проверяем эмитацию события
  expect(wrapper.emitted()).toHaveProperty('update:modelValue')
  expect(wrapper.emitted('update:modelValue')[0]).toEqual(['Hello Vue'])
})

Разбор кода:

  • wrapper.find('input') ищет элемент <input> внутри компонента.
  • setValue('Hello Vue') не только меняет значение поля, но и вызывает событие input внутри компонента.
  • wrapper.emitted() возвращает объект всех эмитированных событий.
  • Проверка toHaveProperty('update:modelValue') подтверждает факт эмита.
  • wrapper.emitted('update:modelValue')[0] возвращает массив аргументов первого вызова события.

Тестирование с начальным modelValue

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

test('инициализируется с переданным modelValue', () => {
  const wrapper = mount(MyInput, {
    props: {
      modelValue: 'Initial'
    }
  })

  const input = wrapper.find('input')
  expect(input.element.value).toBe('Initial')
})

Это гарантирует соответствие внутреннего состояния компонента значению пропса.

Множественные изменения и асинхронные обновления

Если компонент эмитирует несколько событий при последовательных действиях:

test('эмитирует несколько раз при последовательном вводе', async () => {
  const wrapper = mount(MyInput)
  const input = wrapper.find('input')

  await input.setValue('A')
  await input.setValue('AB')
  await input.setValue('ABC')

  const emitted = wrapper.emitted('update:modelValue')
  expect(emitted).toHaveLength(3)
  expect(emitted.map(e => e[0])).toEqual(['A', 'AB', 'ABC'])
})

Важные моменты:

  • Каждое событие хранится как отдельный элемент массива.
  • Аргументы события всегда оборачиваются в массив [значение].
  • Асинхронность требует использования await, чтобы Vue успел обработать реактивные обновления.

Тестирование кастомных компонентов с v-model

Компоненты могут использовать v-model для нестандартных типов данных:

<MyCheckbox v-model="checked" />

Тестирование выглядит аналогично:

test('эмитирует update:modelValue для чекбокса', async () => {
  const wrapper = mount(MyCheckbox)
  const checkbox = wrapper.find('input[type="checkbox"]')

  await checkbox.setChecked(true)
  expect(wrapper.emitted('update:modelValue')[0]).toEqual([true])

  await checkbox.setChecked(false)
  expect(wrapper.emitted('update:modelValue')[1]).toEqual([false])
})

setChecked автоматически вызывает соответствующее событие change и проверка эмита становится прямой.

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

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

const Parent = {
  template: `<MyInput v-model="text" />`,
  components: { MyInput },
  data() {
    return { text: '' }
  }
}

test('родительский data обновляется при эмите', async () => {
  const wrapper = mount(Parent)
  const input = wrapper.findComponent(MyInput).find('input')

  await input.setValue('Vue Test Utils')
  expect(wrapper.vm.text).toBe('Vue Test Utils')
})

Таким образом проверяется не только эмит события, но и реактивное обновление данных родителя.

Советы по стабильному тестированию update:modelValue

  • Использовать setValue или setChecked вместо ручного вызова $emit, чтобы тест имитировал реальное поведение пользователя.
  • Проверять массив аргументов события, а не просто факт эмита.
  • Для асинхронных изменений всегда использовать await с методами Vue Test Utils.
  • При сложных компонентах с несколькими v-model удобно использовать именованные события: update:modelValue и update:otherValue.

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