Тест в экосистеме Vue строится вокруг воспроизводимого сценария: подготовка окружения, выполнение действия и проверка результата. Эта структура позволяет писать изолированные, предсказуемые и легко поддерживаемые тесты компонентов с использованием Vue Test Utils и любого раннера (Vitest, Jest).
Этап подготовки формирует исходное состояние теста. Здесь создаётся экземпляр компонента, подменяются зависимости, задаются входные данные и конфигурация окружения.
Ключевая операция — mount или
shallowMount.
import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
const wrapper = mount(MyComponent)
Выбор зависит от цели теста: поведенческий тест компонента или интеграционная проверка связки.
Исходные данные компонента задаются через props.
const wrapper = mount(MyComponent, {
props: {
title: 'Test title',
isActive: true
}
})
Важно формировать props так, чтобы тест отражал конкретный сценарий, а не универсальный случай.
Vue Test Utils предоставляет объект global для настройки
окружения:
const wrapper = mount(MyComponent, {
global: {
plugins: [store, router],
stubs: ['RouterLink'],
mocks: {
$t: (key) => key
},
provide: {
apiUrl: 'https://example.test'
}
}
})
$t, $axios).provide/inject.Если компонент использует хранилище состояния, оно инициализируется заранее:
import { createPinia, setActivePinia } from 'pinia'
setActivePinia(createPinia())
Такая инициализация должна происходить до монтирования компонента, иначе состояние будет некорректным.
На этом этапе воспроизводится пользовательское или программное действие, которое изменяет состояние компонента.
Vue Test Utils предоставляет методы-обёртки для событий:
await wrapper.find('button').trigger('click')
await обязателен, так как Vue обновляет DOM
асинхронно.
Поддерживаются все стандартные события:
await wrapper.find('input').setValue('hello')
await wrapper.find('form').trigger('submit')
Метод setValue автоматически эмитит input и
change.
Если компонент предоставляет методы через methods или
defineExpose, они вызываются напрямую:
wrapper.vm.increment()
await wrapper.vm.$nextTick()
Такой подход полезен для тестирования логики, не связанной напрямую с DOM.
При использовании shallowMount часто требуется
имитировать события:
wrapper.findComponent({ name: 'ChildComponent' })
.vm.$emit('submit', payload)
Это позволяет проверить реакцию родительского компонента без реального рендера дочернего.
При наличии async/await, setTimeout,
запросов к API требуется дождаться завершения всех микротасок:
await flushPromises()
flushPromises синхронизирует очередь промисов, устраняя
нестабильность тестов.
Этап проверки фиксирует ожидаемый эффект: изменение DOM, состояния, эмиссию событий или вызов побочных эффектов.
expect(wrapper.text()).toContain('Success')
expect(wrapper.find('.error').exists()).toBe(false)
Рекомендуется проверять результат, а не конкретную реализацию. Например, наличие текста вместо точной HTML-структуры.
expect(wrapper.props('isActive')).toBe(true)
expect(wrapper.vm.count).toBe(1)
Проверка внутреннего состояния допустима, если оно является частью публичного поведения компонента.
const events = wrapper.emitted()
expect(events.submit).toHaveLength(1)
expect(events.submit[0]).toEqual([{ value: 42 }])
emitted() возвращает объект, где ключ — имя события,
значение — массив аргументов по каждому вызову.
Для функций-заглушек используется стандартный механизм моков тест-раннера:
const saveMock = vi.fn()
const wrapper = mount(MyComponent, {
global: {
mocks: {
$save: saveMock
}
}
})
await wrapper.find('button').trigger('click')
expect(saveMock).toHaveBeenCalledWith({ id: 1 })
Такая проверка особенно важна для побочных эффектов: логирования, навигации, HTTP-запросов.
Чёткое разделение на setup, execute,
assert повышает читаемость и снижает стоимость
поддержки.
it('отправляет форму', async () => {
// setup
const wrapper = mount(FormComponent)
// execute
await wrapper.find('input').setValue('test')
await wrapper.find('form').trigger('submit')
// assert
expect(wrapper.emitted('submit')).toBeTruthy()
})
Такой формат облегчает поиск ошибок: проблема либо в подготовке, либо в действии, либо в ожиданиях.
Каждый тест должен быть независимым:
Использование beforeEach допустимо для общей подготовки,
но логика теста должна оставаться локальной и очевидной.
Монтирование запускает setup, created,
mounted. Триггеры событий вызывают обновления
(watch, computed, updated).
Проверки должны учитывать, на каком этапе находится компонент.
При необходимости проверки промежуточного состояния используется
$nextTick или ожидание промисов, чтобы синхронизироваться с
реактивной системой Vue.
Жизненный цикл теста setup → execute → assert формирует
устойчивый каркас для проверки Vue-компонентов. Он минимизирует неявные
зависимости, делает тесты читаемыми и позволяет точно отражать поведение
приложения в контролируемом окружении.