В современных приложениях на Vue часто возникает необходимость динамически подгружать компоненты в зависимости от состояния приложения или пользовательского ввода. Тестирование таких компонентов требует особого подхода, так как их рендер может зависеть от пропсов, слотов, асинхронных данных или глобального состояния. Vue Test Utils предоставляет инструменты для глубокого контроля над рендерингом и взаимодействием с динамическими компонентами.
Для тестирования динамических компонентов используется метод
mount или shallowMount из Vue Test Utils.
Динамическим компонентом может быть обычный компонент Vue, переданный
через :is:
import { mount } from '@vue/test-utils'
import DynamicComponent from '@/components/DynamicComponent.vue'
import ComponentA from '@/components/ComponentA.vue'
import ComponentB from '@/components/ComponentB.vue'
describe('DynamicComponent', () => {
it('рендерит правильный компонент в зависимости от пропса', () => {
const wrapperA = mount(DynamicComponent, {
props: { componentType: 'A' },
global: { components: { ComponentA, ComponentB } }
})
expect(wrapperA.findComponent(ComponentA).exists()).toBe(true)
const wrapperB = mount(DynamicComponent, {
props: { componentType: 'B' },
global: { components: { ComponentA, ComponentB } }
})
expect(wrapperB.findComponent(ComponentB).exists()).toBe(true)
})
})
Ключевой момент: для корректного рендеринга Vue должен знать о
компонентах, которые могут использоваться динамически. Это достигается
через global.components при монтировании в тесте.
Vue поддерживает асинхронные компоненты, которые загружаются по
требованию. Для их тестирования необходимо использовать асинхронное
ожидание рендера через nextTick или
flushPromises:
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import AsyncDynamicComponent from '@/components/AsyncDynamicComponent.vue'
jest.mock('@/components/ComponentC.vue', () => ({
template: '<div>Компонент C</div>'
}))
it('асинхронно рендерит компонент', async () => {
const wrapper = mount(AsyncDynamicComponent, {
props: { componentType: 'C' }
})
await nextTick()
expect(wrapper.text()).toContain('Компонент C')
})
Для асинхронных компонентов важно убедиться, что тест дожидается
полной загрузки компонента перед проверкой DOM.
flushPromises используется для промисов внутри компонентов,
а nextTick — для реактивных обновлений Vue.
Динамические компоненты могут получать различные пропсы и эмитить события. Vue Test Utils позволяет тестировать взаимодействия и реакцию на эти изменения:
import { mount } from '@vue/test-utils'
import DynamicComponent from '@/components/DynamicComponent.vue'
import ComponentD from '@/components/ComponentD.vue'
it('передаёт пропсы и обрабатывает события', async () => {
const wrapper = mount(DynamicComponent, {
props: { componentType: 'D', message: 'Тест' },
global: { components: { ComponentD } }
})
const componentD = wrapper.findComponent(ComponentD)
expect(componentD.props('message')).toBe('Тест')
await componentD.vm.$emit('update', 42)
expect(wrapper.emitted('update')[0]).toEqual([42])
})
Особое внимание уделяется проверке того, что динамический компонент корректно получает пропсы и правильно эмитит события, чтобы родительский компонент реагировал на них ожидаемым образом.
Динамические компоненты могут иметь слоты, и их тестирование включает проверку рендера вложенного контента:
import { mount } from '@vue/test-utils'
import DynamicWithSlot from '@/components/DynamicWithSlot.vue'
import ComponentE from '@/components/ComponentE.vue'
it('рендерит слот внутри динамического компонента', () => {
const wrapper = mount(DynamicWithSlot, {
props: { componentType: 'E' },
global: { components: { ComponentE } },
slots: { default: '<span class="slot-content">Слот</span>' }
})
expect(wrapper.find('.slot-content').exists()).toBe(true)
expect(wrapper.find('.slot-content').text()).toBe('Слот')
})
Слоты могут быть как простыми HTML-фрагментами, так и другими компонентами, что делает проверку гибкой и важной для динамических интерфейсов.
Для упрощения тестирования сложных динамических компонентов часто используют заглушки (stubs) или мок-версии компонентов:
const wrapper = mount(DynamicComponent, {
props: { componentType: 'F' },
global: { stubs: ['ComponentF'] }
})
expect(wrapper.findComponent({ name: 'ComponentF' }).exists()).toBe(true)
Стратегия использования stub-компонентов снижает зависимость теста от реализации дочерних компонентов, ускоряет выполнение тестов и делает их более предсказуемыми.
Динамическое обновление компонента часто зависит от реактивного изменения пропсов или состояния:
it('смена компонента при обновлении пропса', async () => {
const wrapper = mount(DynamicComponent, {
props: { componentType: 'A' },
global: { components: { ComponentA, ComponentB } }
})
expect(wrapper.findComponent(ComponentA).exists()).toBe(true)
await wrapper.setProps({ componentType: 'B' })
expect(wrapper.findComponent(ComponentB).exists()).toBe(true)
})
Использование setProps позволяет проверить реакцию Vue
на изменения пропсов и корректность обновления динамического
рендера.
findComponent, а не
через text(), чтобы избежать ложноположительных
результатов.global.components для регистрации всех
потенциальных компонентов, которые могут рендериться.nextTick и
flushPromises для надежного ожидания рендера.stubs или
мок-файлы, чтобы тестировать только логику родителя.Тщательная настройка монтирования, корректное ожидание асинхронного рендера и внимательная проверка пропсов, событий и слотов позволяют создать надежные и стабильные тесты для динамических компонентов.