Vue Test Utils (VTU) предоставляет инструменты для тестирования
компонентов Vue, включая возможность управления зависимостями через
provide и inject. Этот механизм позволяет
компонентам получать данные или сервисы от родительских компонентов без
необходимости явно передавать их через props, что особенно удобно при
тестировании сложных иерархий компонентов.
provide и
injectprovide используется на родительском
компоненте для предоставления данных или сервисов. Обычно это объект или
функция, возвращающая объект:export default {
provide() {
return {
apiService: this.apiService
}
},
data() {
return {
apiService: new ApiService()
}
}
}
inject позволяет дочернему компоненту
получать эти данные:export default {
inject: ['apiService'],
mounted() {
this.apiService.fetchData()
}
}
VTU позволяет в тестах заменять реальные зависимости моками или заглушками, что делает тесты изолированными и предсказуемыми.
При использовании VTU для монтирования компонентов можно передавать
значения provide через опцию global.provide.
Это особенно полезно для подмены сервисов и зависимостей:
import { mount } from '@vue/test-utils'
import ChildComponent from '@/components/ChildComponent.vue'
const mockApiService = {
fetchData: jest.fn().mockResolvedValue({ data: 'test' })
}
const wrapper = mount(ChildComponent, {
global: {
provide: {
apiService: mockApiService
}
}
})
await wrapper.vm.apiService.fetchData()
expect(mockApiService.fetchData).toHaveBeenCalled()
В этом примере дочерний компонент получает мок
apiService, что позволяет полностью контролировать
поведение теста без обращения к реальной реализации.
Если provide в компоненте реализован как функция, VTU
корректно вызывает её и позволяет переопределять значения:
const wrapper = mount(ChildComponent, {
global: {
provide: () => ({
apiService: {
fetchData: jest.fn()
}
})
}
})
Использование функции позволяет динамически формировать зависимости и создавать разные конфигурации для разных тестов.
Vue Test Utils поддерживает возможность подмены глобальных
зависимостей через опцию global.mocks. Разница с
provide заключается в том, что mocks
используется для мокирования глобальных свойств, таких как
$router, $store или $t в i18n,
тогда как provide/inject имитирует передачу сервисов через
иерархию компонентов.
Пример подмены $store:
const wrapper = mount(ComponentUnderTest, {
global: {
mocks: {
$store: {
state: { user: { name: 'Test' } },
dispatch: jest.fn()
}
}
}
})
provide/inject используется для более гибкого и
изолированного тестирования сервисов, которые напрямую не связаны с
глобальным состоянием приложения.
В проектах с глубокой вложенностью компонентов часто нужно проверять
поведение дочерних компонентов, которые зависят от provide
родителя. VTU позволяет создавать иерархию компонентов в тестах, при
этом замещая конкретные зависимости:
import ParentComponent from '@/components/ParentComponent.vue'
import ChildComponent from '@/components/ChildComponent.vue'
const wrapper = mount(ParentComponent, {
global: {
provide: {
apiService: { fetchData: jest.fn() }
},
stubs: {
ChildComponent: true
}
}
})
С помощью stubs можно изолировать дочерние компоненты,
тестируя родительский компонент независимо, или наоборот, тестировать
дочерний компонент, передавая нужные зависимости через
global.provide.
provide
наследуются от родителя к детям, поэтому дочерние компоненты
автоматически получают данные от ближайшего родителя, если не передан
локальный override.provide, передаются по ссылке. Изменение их состояния
в тесте может повлиять на другие компоненты.global.mocks и
global.provide могут использоваться одновременно для полной
подмены зависимостей, что позволяет тестировать компонент в максимально
контролируемой среде.Проверка вызова метода зависимости:
test('должен вызвать fetchData при монтировании', async () => {
const fetchDataMock = jest.fn()
const wrapper = mount(ChildComponent, {
global: {
provide: {
apiService: { fetchData: fetchDataMock }
}
}
})
await wrapper.vm.$nextTick()
expect(fetchDataMock).toHaveBeenCalled()
})
Проверка реактивности provide:
import { ref } from 'vue'
test('реактивное значение provide обновляется в дочернем компоненте', async () => {
const reactiveValue = ref('initial')
const wrapper = mount(ChildComponent, {
global: {
provide: { reactiveValue }
}
})
expect(wrapper.text()).toContain('initial')
reactiveValue.value = 'updated'
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('updated')
})
Эти подходы позволяют полностью изолировать компонент и тестировать его поведение при различных конфигурациях зависимостей, обеспечивая высокое качество модульного тестирования.