Тестирование двусторонней привязки

Двусторонняя привязка данных в Vue реализуется через директиву v-model. Она позволяет синхронизировать значение состояния компонента с пользовательским интерфейсом. При изменении данных в модели автоматически обновляется отображение, и наоборот — изменение в UI отражается в состоянии компонента. Тестирование двусторонней привязки требует проверки корректного взаимодействия модели и представления.

Использование Vue Test Utils для тестирования v-model

Vue Test Utils предоставляет набор инструментов для изолированного тестирования компонентов. Основной объект — mount или shallowMount, позволяющий создавать экземпляры компонента и работать с их данными и DOM.

Пример базового подхода:

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

test('двусторонняя привязка через v-model', async () => {
  const wrapper = mount(MyInput, {
    props: {
      modelValue: ''
    }
  })

  const input = wrapper.find('input')
  
  // Проверка начального значения
  expect(input.element.value).toBe('')

  // Изменение значения через модель
  await wrapper.setProps({ modelValue: 'тест' })
  expect(input.element.value).toBe('тест')

  // Изменение значения через UI
  await input.setValue('новое значение')
  expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['новое значение'])
})

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

  • setProps позволяет проверять, как изменения во внешних свойствах влияют на компонент.
  • setValue эмулирует ввод пользователя и автоматически инициирует событие input.
  • Проверка emitted() фиксирует, что компонент правильно эмиттирует событие update:modelValue.

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

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

  1. Правильность начального состояния
  2. Корректное обновление через пропсы
  3. Корректное эмитирование событий при изменении UI
  4. Интеграцию с вычисляемыми свойствами и watch

Пример теста для компонента с числовым вводом и форматированием:

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

test('форматирование и двусторонняя привязка', async () => {
  const wrapper = mount(NumericInput, {
    props: { modelValue: 0 }
  })

  const input = wrapper.find('input')

  // Обновление через UI
  await input.setValue('1234')
  expect(wrapper.emitted()['update:modelValue'][0]).toEqual([1234])

  // Обновление через props
  await wrapper.setProps({ modelValue: 5678 })
  expect(input.element.value).toBe('5,678') // предполагаемое форматирование
})

Здесь проверяется не только передача значения в модель, но и корректное отображение с форматированием.

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

Vue 3 позволяет определять кастомные модели с пользовательскими событиями и пропсами. В таких случаях важно явно указывать имя события и проверять его вызовы:

<template>
  <input :value="customValue" @input="$emit('change', $event.target.value)">
</template>

<script>
export default {
  props: ['customValue']
}
</script>

Тест:

const wrapper = mount(CustomInput, {
  props: { customValue: '' }
})
const input = wrapper.find('input')
await input.setValue('abc')
expect(wrapper.emitted().change[0]).toEqual(['abc'])

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

Частые ошибки при тестировании двусторонней привязки

  • Неправильное использование setValue: для v-model важно использовать именно setValue, чтобы сгенерировалось событие input или кастомное событие.
  • Игнорирование асинхронности: все изменения через setProps и setValue должны сопровождаться await, иначе DOM может быть не обновлён.
  • Проверка element.value вместо emitted: изменение через UI требует проверки эмитированных событий, иначе тест не подтверждает правильную работу модели.
  • Непроверка начального состояния: пропущенная проверка начального значения часто приводит к скрытым багам.

Стратегии комплексного тестирования

  1. Юнит-тесты: проверка отдельного компонента и всех его взаимодействий через v-model.
  2. Интеграционные тесты: проверка передачи данных между родительским и дочерним компонентом с использованием v-model.
  3. Энд-ту-энд тесты: проверка пользовательского поведения, когда изменения UI должны привести к обновлению состояния приложения.

Использование Vue Test Utils позволяет изолировать каждый уровень и гарантировать корректную работу двусторонней привязки независимо от сложности компонента.

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

Важно учитывать, что двусторонняя привязка напрямую зависит от реактивности Vue. При тестировании реактивных объектов или массивов следует:

  • Использовать reactive или ref при передаче пропсов в тесте.
  • После изменения данных вызывать await nextTick() для гарантии обновления DOM.

Пример с массивом:

import { ref, nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import ListInput from '@/components/ListInput.vue'

test('двусторонняя привязка с массивом', async () => {
  const items = ref(['a', 'b'])
  const wrapper = mount(ListInput, {
    props: { modelValue: items.value }
  })

  await wrapper.find('input').setValue('c')
  await nextTick()
  expect(wrapper.emitted()['update:modelValue'][0]).toEqual([['a','b','c']])
})

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