Условный рендеринг — ключевой механизм в Vue, позволяющий отображать разные компоненты в зависимости от состояния приложения или переданных пропсов. При тестировании компонентов с помощью Vue Test Utils важно проверять как правильность рендеринга, так и корректность взаимодействия между условиями, компонентами и их состояниями.
В Vue условный рендеринг чаще всего реализуется с помощью директив
v-if, v-else-if, v-else или
v-show.
<template>
<div>
<ComponentA v-if="type === 'A'" />
<ComponentB v-else-if="type === 'B'" />
<ComponentC v-else />
</div>
</template>
<script>
export default {
props: {
type: {
type: String,
required: true
}
},
components: { ComponentA, ComponentB, ComponentC }
}
</script>
В данном примере компонент, который будет отрендерен, полностью
зависит от значения пропса type. Это накладывает требования
на тестирование: необходимо проверять все возможные ветви условного
рендеринга.
Для тестирования условного рендеринга используется функция
mount или shallowMount из Vue Test Utils.
Разница между ними в том, что shallowMount рендерит
компоненты только поверхностно, заменяя дочерние компоненты
заглушками.
import { mount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent.vue'
import ComponentA from '@/components/ComponentA.vue'
import ComponentB from '@/components/ComponentB.vue'
import ComponentC from '@/components/ComponentC.vue'
describe('ParentComponent', () => {
it('рендерит ComponentA при type A', () => {
const wrapper = mount(ParentComponent, {
props: { type: 'A' }
})
expect(wrapper.findComponent(ComponentA).exists()).toBe(true)
expect(wrapper.findComponent(ComponentB).exists()).toBe(false)
expect(wrapper.findComponent(ComponentC).exists()).toBe(false)
})
it('рендерит ComponentB при type B', () => {
const wrapper = mount(ParentComponent, {
props: { type: 'B' }
})
expect(wrapper.findComponent(ComponentB).exists()).toBe(true)
expect(wrapper.findComponent(ComponentA).exists()).toBe(false)
expect(wrapper.findComponent(ComponentC).exists()).toBe(false)
})
it('рендерит ComponentC при других значениях', () => {
const wrapper = mount(ParentComponent, {
props: { type: 'C' }
})
expect(wrapper.findComponent(ComponentC).exists()).toBe(true)
expect(wrapper.findComponent(ComponentA).exists()).toBe(false)
expect(wrapper.findComponent(ComponentB).exists()).toBe(false)
})
})
Компоненты с условным рендерингом часто меняют вид в процессе работы
приложения. Vue Test Utils позволяет эмулировать такие изменения через
реактивные свойства или изменение пропсов с использованием
setProps.
it('должен менять рендер при изменении пропса', async () => {
const wrapper = mount(ParentComponent, { props: { type: 'A' } })
expect(wrapper.findComponent(ComponentA).exists()).toBe(true)
await wrapper.setProps({ type: 'B' })
expect(wrapper.findComponent(ComponentB).exists()).toBe(true)
expect(wrapper.findComponent(ComponentA).exists()).toBe(false)
})
Использование await важно, так как Vue обновляет DOM
асинхронно, и без него проверка может происходить до фактического
ререндеринга.
Иногда рендер зависит от нескольких условий, например пропсов и
состояния компонента (data или computed). В
таких случаях тесты должны учитывать все комбинации условий.
<template>
<div>
<ComponentX v-if="isActive && type === 'X'" />
<ComponentY v-else-if="!isActive && type === 'Y'" />
<ComponentZ v-else />
</div>
</template>
Тестирование всех веток:
it('рендерит ComponentX только если isActive и type X', async () => {
const wrapper = mount(ParentComponent, {
props: { type: 'X' },
data: () => ({ isActive: true })
})
expect(wrapper.findComponent(ComponentX).exists()).toBe(true)
await wrapper.setData({ isActive: false })
expect(wrapper.findComponent(ComponentZ).exists()).toBe(true)
})
findComponent и селекторовДля проверки рендеринга разных компонентов можно использовать не только компоненты, но и классы, атрибуты или селекторы.
expect(wrapper.find('.component-x').exists()).toBe(true)
expect(wrapper.find('[data-test="component-y"]').exists()).toBe(false)
Использование атрибутов data-test особенно полезно для
тестов, так как они не зависят от структуры CSS и менее подвержены
изменениям верстки.
При глубоком тестировании условного рендеринга часто необходимо
изолировать родительский компонент. Для этого можно использовать
shallowMount или моки:
const wrapper = shallowMount(ParentComponent, {
props: { type: 'A' },
global: {
stubs: {
ComponentA: true,
ComponentB: true,
ComponentC: true
}
}
})
Это позволяет проверять только логику рендеринга без влияния внутренней реализации дочерних компонентов.
Сложные компоненты могут использовать слоты с условным содержимым:
<template>
<div>
<slot v-if="showHeader" name="header" />
<slot />
</div>
</template>
Тестирование слотов:
const wrapper = mount(ParentComponent, {
props: { showHeader: true },
slots: { header: '<h1>Заголовок</h1>', default: '<p>Контент</p>' }
})
expect(wrapper.html()).toContain('<h1>Заголовок</h1>')
expect(wrapper.html()).toContain('<p>Контент</p>')
Если showHeader становится false, слот с
именем header не должен рендериться.
v-if, v-else-if, v-else
должна иметь отдельный тест.data-test для точного
таргетинга элементов.Эти подходы позволяют обеспечить надежное покрытие и выявлять ошибки условного рендеринга до возникновения проблем в пользовательском интерфейсе.