Vue Test Utils (VTU) является официальной библиотекой для юнит-тестирования компонентов Vue. Она обеспечивает создание обёрток компонентов, доступ к их данным, методам и жизненному циклу, а также симуляцию событий. Несмотря на схожесть API, конфигурация для Vue 3 и Vue 2 имеет свои особенности, которые необходимо учитывать для корректного тестирования.
Для Vue 3:
npm install @vue/test-utils@next --save-dev
npm install vue@3
Для Vue 2:
npm install @vue/test-utils --save-dev
npm install vue@2
Важное отличие — для Vue 3 используется пакет
@vue/test-utils@next, который поддерживает Composition API
и новые методы жизненного цикла.
Глобальная конфигурация позволяет задать общие настройки для всех тестов: плагины, компоненты, директивы, mocks и stubs.
Для Vue 3:
import { config } from '@vue/test-utils'
import MyPlugin from '@/plugins/MyPlugin'
config.global.plugins = [MyPlugin]
config.global.components = {
'MyButton': MyButton
}
config.global.mocks = {
$t: (msg) => msg // mock для i18n
}
config.global.stubs = {
'router-link': true
}
config.global.directives = {
focus: {
mounted(el) {
el.focus()
}
}
}
Для Vue 2 конфигурация немного отличается:
import Vue from 'vue'
import { config } from '@vue/test-utils'
import MyPlugin from '@/plugins/MyPlugin'
Vue.use(MyPlugin)
config.stubs = {
'router-link': true
}
config.mocks = {
$t: (msg) => msg
}
config.logModifiedComponents = false
Особенности:
config.global.Vue.use(), а
глобальные компоненты через Vue.component().Vue.directive().mount создаёт полноценный экземпляр компонента с
рендером всех дочерних компонентов. shallowMount рендерит
компонент, но заменяет все дочерние на заглушки (stubs).
Пример для Vue 3:
import { mount, shallowMount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
const wrapper = mount(MyComponent, {
global: {
plugins: [MyPlugin],
mocks: { $t: (msg) => msg },
stubs: ['ChildComponent']
}
})
const shallowWrapper = shallowMount(MyComponent, {
global: {
stubs: ['ChildComponent']
}
})
Для Vue 2:
import { mount, shallowMount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
const wrapper = mount(MyComponent, {
mocks: { $t: (msg) => msg },
stubs: ['ChildComponent']
})
const shallowWrapper = shallowMount(MyComponent, {
stubs: ['ChildComponent']
})
Ключевое отличие: Vue 3 использует объект
global для всех глобальных настроек, тогда как Vue 2
напрямую передаёт mocks и stubs в параметры.
Для интеграции VTU с Jest требуется корректная настройка:
Vue 3 + Jest:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'json', 'vue'],
transform: {
'^.+\\.vue$': '@vue/vue3-jest',
'^.+\\.js$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
}
Vue 2 + Jest:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'json', 'vue'],
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.js$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
}
Отличия трансформеров:
@vue/vue3-jest, поддерживающий
Composition API.vue-jest, совместимый с Options
API.Mocks позволяют подменять глобальные свойства, такие как
$router, $store, $axios.
Vue 3:
const wrapper = mount(MyComponent, {
global: {
mocks: {
$store: { state: { count: 0 }, commit: jest.fn() }
},
provide: {
api: { fetchData: jest.fn() }
}
}
})
Vue 2:
const wrapper = mount(MyComponent, {
mocks: {
$store: { state: { count: 0 }, commit: jest.fn() }
},
provide: {
api: { fetchData: jest.fn() }
}
})
Замечание: в Vue 3 provide и
inject полностью интегрированы с Composition API, что
упрощает тестирование зависимостей.
Stubs позволяют заменить дочерние компоненты на заглушки:
Vue 3:
shallowMount(MyComponent, {
global: {
stubs: {
'ChildComponent': { template: '<div class="stub"></div>' },
'AnotherChild': true // автоматическая заглушка
}
}
})
Vue 2:
shallowMount(MyComponent, {
stubs: {
'ChildComponent': { template: '<div class="stub"></div>' },
'AnotherChild': true
}
})
Особенности:
Vue Test Utils автоматически обрабатывает жизненный цикл компонента:
mount и shallowMount вызывают
beforeMount, mounted.wrapper.setData,
wrapper.setProps синхронизируются с DOM через
nextTick.Пример для Vue 3:
await wrapper.setProps({ visible: true })
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('Контент')
Vue 2:
wrapper.setProps({ visible: true })
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('Контент')
Для Vue 3:
import { mount, VueWrapper } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
let wrapper: VueWrapper<any>
wrapper = mount(MyComponent)
Vue 2:
import { mount, Wrapper } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
let wrapper: Wrapper<Vue>
wrapper = mount(MyComponent)
Отличие: в Vue 3 используется
VueWrapper, а в Vue 2 —
Wrapper<Vue>.
VTU позволяет создавать снимки компонентов для отслеживания изменений разметки:
const wrapper = mount(MyComponent)
expect(wrapper.html()).toMatchSnapshot()
Snapshot-тесты одинаковы для Vue 2 и Vue 3, однако формат HTML в Vue 3 может отличаться из-за нового рендера.
Эта конфигурация обеспечивает гибкое и детализированное тестирование как для Vue 3, так и для Vue 2, учитывая различия в глобальных настройках, плагинах, stubs и mocks. Она создаёт основу для надёжных юнит-тестов с интеграцией в Jest или другие тестовые среды.