v-model является удобным способом связывать данные
между родительским компонентом и дочерним компонентом. В Vue 3 синтаксис
немного отличается от Vue 2, особенно при работе с кастомными
компонентами, где v-model можно настраивать с помощью имени
пропса и события. Тестирование таких компонентов требует понимания того,
как v-model транслируется в пропсы и события, а также как
это отражается на состоянии компонента.
Когда родительский компонент использует v-model для
дочернего компонента, фактически происходит следующее:
modelValue (по умолчанию).update:modelValue испускается
дочерним компонентом для уведомления родителя об изменении
значения.Пример базового компонента:
// InputComponent.vue
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: {
modelValue: String
}
}
</script>
Использование в родительском компоненте:
<InputComponent v-model="text" />
Эквивалентно:
<InputComponent :modelValue="text" @update:modelValue="val => text = val" />
Vue Test Utils предоставляет методы mount и
shallowMount, которые позволяют создавать экземпляры
компонентов и взаимодействовать с ними. Для проверки работы
v-model важно учитывать два аспекта:
modelValue.update:modelValue при изменении внутреннего
состояния компонента.Пример теста для InputComponent:
import { mount } from '@vue/test-utils'
import InputComponent from '@/components/InputComponent.vue'
test('v-model правильно передает и обновляет значение', async () => {
const wrapper = mount(InputComponent, {
props: {
modelValue: 'initial'
}
})
// Проверка передачи начального значения
const input = wrapper.find('input')
expect(input.element.value).toBe('initial')
// Симуляция ввода нового значения
await input.setValue('updated')
// Проверка эмита события
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['updated'])
})
Ключевой момент: setValue автоматически триггерит
событие input, которое в компоненте связано с
$emit('update:modelValue').
Vue 3 позволяет переопределять имя пропса и события, используя аргументы v-model:
<CustomComponent v-model:title="pageTitle" />
Внутри CustomComponent:
props: {
title: String
},
emits: ['update:title']
Тестирование:
import { mount } from '@vue/test-utils'
import CustomComponent from '@/components/CustomComponent.vue'
test('v-model с кастомным именем работает корректно', async () => {
const wrapper = mount(CustomComponent, {
props: { title: 'Hello' }
})
await wrapper.setProps({ title: 'World' })
expect(wrapper.props('title')).toBe('World')
wrapper.vm.$emit('update:title', 'Updated')
expect(wrapper.emitted('update:title')[0]).toEqual(['Updated'])
})
Важно понимать, что при кастомном v-model проверка
всегда должна учитывать именно правильное событие, а не
универсальный update:modelValue.
Для более сложных компонентов, например, с несколькими внутренними
состояниями, тестирование v-model требует:
Пример с компонентом формы:
<template>
<input :value="modelValue.firstName" @input="$emit('update:modelValue', { ...modelValue, firstName: $event.target.value })" />
<input :value="modelValue.lastName" @input="$emit('update:modelValue', { ...modelValue, lastName: $event.target.value })" />
</template>
<script>
export default {
props: {
modelValue: Object
}
}
</script>
Тест:
import { mount } from '@vue/test-utils'
import NameForm from '@/components/NameForm.vue'
test('v-model с объектом обновляет свойства корректно', async () => {
const wrapper = mount(NameForm, {
props: { modelValue: { firstName: 'John', lastName: 'Doe' } }
})
const inputs = wrapper.findAll('input')
await inputs[0].setValue('Jane')
await inputs[1].setValue('Smith')
expect(wrapper.emitted('update:modelValue')[0]).toEqual([{ firstName: 'Jane', lastName: 'Doe' }])
expect(wrapper.emitted('update:modelValue')[1]).toEqual([{ firstName: 'Jane', lastName: 'Smith' }])
})
Такой подход позволяет тестировать v-model не только с
примитивными значениями, но и с объектами и массивами.
shallowMountshallowMount полезен для изоляции компонента и
игнорирования дочерних компонентов. В случае тестирования
v-model это особенно важно:
Пример:
import { shallowMount } from '@vue/test-utils'
import InputComponent from '@/components/InputComponent.vue'
test('shallowMount проверяет эмит события', async () => {
const wrapper = shallowMount(InputComponent, {
props: { modelValue: 'test' }
})
await wrapper.find('input').setValue('new')
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['new'])
})
find или
findComponent.await при setValue или
trigger('input'), чтобы обновления Vue успели
примениться.props полезна для подтверждения корректной
передачи начального значения.wrapper.emitted(),
чтобы убедиться, что компонент правильно уведомляет родителя об
изменениях.v-model на компонентах обеспечивает двустороннее связывание данных, и тестирование через Vue Test Utils строится на проверке правильной передачи пропсов и корректной эмиссии событий обновления. Для сложных компонентов важно симулировать пользовательские действия и проверять, что обновления данных происходят именно так, как это ожидается в реальном приложении.