Vue Test Utils (VTU) предоставляет мощный набор инструментов для
тестирования компонентов Vue 3. При работе с Composition
API и функцией setup подход к тестированию
отличается от привычного Options API. Основной принцип заключается в
том, что логика компонента теперь инкапсулируется в реактивных
переменных, ref и reactive объектах, а также в
функциях, возвращаемых из setup. Тесты должны учитывать эти
особенности для корректного взаимодействия с компонентом.
Для тестирования компонентов с setup используется
функция mount из VTU. Она создает виртуальное представление
компонента и предоставляет доступ к его реактивному состоянию и
DOM-элементам.
import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
test('инкремент счетчика', async () => {
const wrapper = mount(Counter)
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.text()).toContain('1')
})
Ключевые моменты:
mount полностью рендерит компонент, включая дочерние
компоненты.shallowMount используется для изоляции теста от
вложенных компонентов.setup важно использовать
async/await при взаимодействии с событиями, потому что
реактивность обновляется асинхронно.В setup реактивные данные создаются через
ref и reactive. Для проверки их значений можно
использовать доступ к wrapper.vm, где vm — это
экземпляр компонента с реактивными переменными.
import { ref } from 'vue'
const wrapper = mount({
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
},
template: `<button @click="increment">{{ count }}</button>`
})
expect(wrapper.vm.count).toBe(0)
await wrapper.vm.increment()
expect(wrapper.vm.count).toBe(1)
Особенности:
ref хранит значение в свойстве
.value.vm позволяет проверять и модифицировать
состояние без взаимодействия с DOM.reactive объекты тестируются аналогично, через свойства
объекта.computed и watch играют ключевую роль в
логике компонентов на Composition API. Проверка вычисляемых свойств
осуществляется через vm, а реакции watch
проверяются косвенно — через изменение реактивных данных и последующую
проверку DOM или состояния.
import { reactive, computed } from 'vue'
const wrapper = mount({
setup() {
const state = reactive({ count: 0 })
const doubled = computed(() => state.count * 2)
return { state, doubled }
},
template: `<div>{{ doubled }}</div>`
})
expect(wrapper.vm.doubled).toBe(0)
wrapper.vm.state.count = 5
expect(wrapper.vm.doubled).toBe(10)
Важные моменты:
watch можно изменять исходные данные и
проверять побочные эффекты (например, вызов функций или изменение
DOM).Функции, возвращаемые из setup, можно тестировать
напрямую через wrapper.vm. Это позволяет изолированно
проверять бизнес-логику без необходимости триггерить DOM-события.
const wrapper = mount({
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
},
template: `{{ count }}`
})
wrapper.vm.increment()
expect(wrapper.vm.count).toBe(1)
Рекомендации:
Асинхронные операции (async/await,
setTimeout,
API-запросы) требуют ожидания завершения реактивных обновлений. Для этого VTU предоставляетnextTick`
и встроенную поддержку асинхронных операций.
import { nextTick, ref } from 'vue'
const wrapper = mount({
setup() {
const count = ref(0)
const incrementAsync = async () => {
await new Promise(resolve => setTimeout(resolve, 100))
count.value++
}
return { count, incrementAsync }
},
template: `{{ count }}`
})
await wrapper.vm.incrementAsync()
await nextTick()
expect(wrapper.vm.count).toBe(1)
Особенности:
await при тестировании асинхронных
функций.nextTick гарантирует, что DOM обновился после изменения
реактивных данных.setup активно использует контекст компонента через
provide и inject. В тестах VTU позволяет
передавать зависимости через опции global.provide.
const wrapper = mount(MyComponent, {
global: {
provide: {
api: { fetchData: () => Promise.resolve('data') }
}
}
})
Принципы:
setup возвращает функции и реактивные данные, но эмиты
событий работают аналогично Options API через
wrapper.emitted().
const wrapper = mount({
setup(_, { emit }) {
const send = () => emit('update', 42)
return { send }
},
template: `<button @click="send">Send</button>`
})
await wrapper.find('button').trigger('click')
expect(wrapper.emitted().update[0]).toEqual([42])
Особенности:
wrapper.emitted() возвращает объект с массивами
аргументов для каждого события.При тестировании компонентов с Composition API важно проверять
рендеринг слотов. VTU поддерживает передачу слотов через опцию
slots при монтировании.
const wrapper = mount(MyComponent, {
slots: {
default: '<span class="slot-content">Hello</span>'
}
})
expect(wrapper.find('.slot-content').exists()).toBe(true)
Принцип:
setup.wrapper.vm.await и
nextTick при изменении данных.setup позволяет изолировать
бизнес-логику от DOM.Тестирование компонентов с Composition API при правильном подходе обеспечивает высокую уверенность в стабильности и корректности логики Vue-приложений.