Во Vue компоненты могут содержать вложенные компоненты и слоты, что позволяет создавать гибкую и переиспользуемую структуру интерфейса. При тестировании важно понимать, как правильно изолировать тестируемый компонент и при этом корректно проверять его взаимодействие с дочерними компонентами и слотами.
Вложенный компонент — это компонент, который
используется внутри другого компонента через импорт и регистрацию в
components.
Слот — область в компоненте, куда можно вставить произвольное содержимое из родительского компонента. Слоты бывают:
Vue Test Utils предоставляет инструменты для рендеринга компонентов с слотами и проверки их поведения.
Для тестирования компонента с дочерними компонентами используется
функция mount или shallowMount:
mount полностью рендерит компонент с его шаблоном и
всеми дочерними компонентами.shallowMount заменяет дочерние компоненты на
заглушки (stubs), что ускоряет тест и изолирует
компонент.Пример:
import { mount, shallowMount } from '@vue/test-utils';
import ParentComponent from '@/components/ParentComponent.vue';
import ChildComponent from '@/components/ChildComponent.vue';
describe('ParentComponent', () => {
it('рендерит дочерний компонент', () => {
const wrapper = mount(ParentComponent);
expect(wrapper.findComponent(ChildComponent).exists()).toBe(true);
});
it('использует shallowMount для изоляции', () => {
const wrapper = shallowMount(ParentComponent);
expect(wrapper.findComponent(ChildComponent).exists()).toBe(true);
expect(wrapper.findComponent(ChildComponent).isVisible()).toBe(false);
});
});
findComponent позволяет искать вложенный компонент по
самому компоненту или селектору.isVisible проверяет видимость элемента на
странице.Слоты передаются в mount через опцию slots.
Это позволяет подставлять контент при тестировании.
Пример с default-слотом:
import { mount } from '@vue/test-utils';
import CardComponent from '@/components/CardComponent.vue';
describe('CardComponent', () => {
it('рендерит контент в default-слоте', () => {
const wrapper = mount(CardComponent, {
slots: {
default: '<p>Тестовый контент</p>'
}
});
expect(wrapper.html()).toContain('Тестовый контент');
});
});
Пример с именованными слотами:
import { mount } from '@vue/test-utils';
import LayoutComponent from '@/components/LayoutComponent.vue';
describe('LayoutComponent', () => {
it('рендерит контент в named slot', () => {
const wrapper = mount(LayoutComponent, {
slots: {
header: '<h1>Заголовок</h1>',
footer: '<footer>Футер</footer>'
}
});
expect(wrapper.find('h1').text()).toBe('Заголовок');
expect(wrapper.find('footer').text()).toBe('Футер');
});
});
Scoped слоты позволяют передавать данные из дочернего компонента в родительский:
import { mount } from '@vue/test-utils';
import ListComponent from '@/components/ListComponent.vue';
describe('ListComponent', () => {
it('рендерит scoped slot с данными', () => {
const wrapper = mount(ListComponent, {
slots: {
item: `<template #item="{ item }"><span>{{ item.name }}</span></template>`
},
props: {
items: [{ name: 'Элемент 1' }, { name: 'Элемент 2' }]
}
});
const items = wrapper.findAll('span');
expect(items[0].text()).toBe('Элемент 1');
expect(items[1].text()).toBe('Элемент 2');
});
});
Для упрощения тестирования больших компонентов удобно использовать stubs. Они позволяют заменить реальный компонент на упрощённый шаблон.
import { mount } from '@vue/test-utils';
import ParentComponent from '@/components/ParentComponent.vue';
import ChildComponent from '@/components/ChildComponent.vue';
describe('ParentComponent с stub', () => {
it('заменяет дочерний компонент заглушкой', () => {
const wrapper = mount(ParentComponent, {
global: {
stubs: {
ChildComponent: '<div class="stub-child"></div>'
}
}
});
expect(wrapper.find('.stub-child').exists()).toBe(true);
});
});
global.stubs можно использовать для любого количества
вложенных компонентов.При тестировании компонентов со слотами и вложенными компонентами важно проверять props и события:
import { mount } from '@vue/test-utils';
import ChildComponent from '@/components/ChildComponent.vue';
import ParentComponent from '@/components/ParentComponent.vue';
describe('ParentComponent', () => {
it('передаёт пропсы в дочерний компонент', () => {
const wrapper = mount(ParentComponent);
const child = wrapper.findComponent(ChildComponent);
expect(child.props('title')).toBe('Заголовок');
});
it('обрабатывает событие от дочернего компонента', async () => {
const wrapper = mount(ParentComponent);
const child = wrapper.findComponent(ChildComponent);
await child.vm.$emit('custom-event', 123);
expect(wrapper.emitted('child-event')[0]).toEqual([123]);
});
});
props() возвращает объект пропсов компонента.emitted() позволяет проверить, были ли вызваны события
и с какими данными.shallowMount для изоляции тестируемого
компонента от сложных вложенных компонентов.<template #slotName="{ data }"> и передавать данные
через props дочернего компонента.Эти подходы позволяют создавать точные, детализированные тесты для компонентов Vue с любым уровнем вложенности и сложными слотами, обеспечивая надежность и предсказуемость работы интерфейса.