Рекурсивные компоненты в Vue представляют собой компоненты, которые внутри своего шаблона вызывают сами себя. Такой подход часто используется для отображения вложенных структур данных, например, деревьев файлов или меню с неограниченным уровнем вложенности. Тестирование таких компонентов требует особого подхода, поскольку стандартные методы рендеринга могут привести к бесконечной рекурсии, если не ограничить глубину или не использовать специальные техники мока.
Для тестирования рекурсивного компонента ключевым моментом является
корректная настройка глубины монтирования. Vue Test Utils предоставляет
методы mount и shallowMount.
mount рендерит компонент полностью,
включая все дочерние компоненты.shallowMount заменяет дочерние
компоненты на заглушки, что предотвращает бесконечную рекурсию при
рекурсивных вызовах.Пример рекурсивного компонента:
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
<RecursiveTree v-if="item.children" :items="item.children" />
</li>
</ul>
</template>
<script>
export default {
name: 'RecursiveTree',
props: {
items: {
type: Array,
required: true
}
}
}
</script>
Для тестирования можно использовать shallowMount, чтобы
избежать полной рекурсии:
import { shallowMount } from '@vue/test-utils'
import RecursiveTree from '@/components/RecursiveTree.vue'
const items = [
{ id: 1, name: 'Root', children: [{ id: 2, name: 'Child' }] }
]
const wrapper = shallowMount(RecursiveTree, {
props: { items }
})
В этом случае дочерний вызов RecursiveTree будет заменен
заглушкой, что позволяет проверить структуру верхнего уровня без
рендеринга всех вложенных уровней.
Если требуется проверить корректный рендер вложенных элементов, можно
использовать параметр stubs для замены рекурсивного вызова
на ограниченное число уровней. Например:
const wrapper = mount(RecursiveTree, {
props: { items },
global: {
stubs: {
RecursiveTree: {
template: '<div class="stub-recursive"></div>'
}
}
}
})
Это позволяет контролировать количество рекурсивных рендеров и проверять, что дочерние элементы отображаются корректно.
Vue Test Utils предоставляет методы для обхода элементов:
find и findAll — поиск элементов по
селектору.text и html — получение содержимого.props и attributes — проверка переданных
данных.Пример проверки структуры дерева:
import { mount } from '@vue/test-utils'
const wrapper = mount(RecursiveTree, { props: { items } })
// Проверка наличия корневого элемента
expect(wrapper.find('li').text()).toContain('Root')
// Проверка наличия дочернего элемента через заглушку
expect(wrapper.find('.stub-recursive').exists()).toBe(true)
Использование заглушек позволяет убедиться, что рекурсивные вызовы присутствуют в дереве компонентов, не погружаясь в бесконечную рекурсию.
Рекурсивные компоненты часто содержат интерактивные элементы,
например, раскрытие/сворачивание узлов. Для их тестирования Vue Test
Utils предоставляет методы trigger и
emitted:
const wrapper = mount(RecursiveTree, { props: { items } })
// Симуляция клика на элемент
await wrapper.find('li').trigger('click')
// Проверка события
expect(wrapper.emitted('SELECT')).toBeTruthy()
expect(wrapper.emitted('select')[0]).toEqual([items[0]])
Такой подход позволяет убедиться, что рекурсивная структура корректно обрабатывает события на любом уровне.
Если рекурсивный компонент получает данные асинхронно, важно
использовать nextTick или flushPromises, чтобы
дождаться обновления DOM:
import { nextTick } FROM 'vue'
await nextTick()
expect(wrapper.findAll('li').length).toBe(2)
Это гарантирует корректную проверку элементов после загрузки данных и предотвращает ложные отрицательные результаты тестов.
shallowMount для верхнего
уровня — минимизирует время выполнения и предотвращает
бесконечную рекурсию.Рекурсивные компоненты могут использовать слоты для кастомизации каждого узла:
<RecursiveTree :items="items">
<template #node="{ item }">
<span class="custom-node">{{ item.name }}</span>
</template>
</RecursiveTree>
Тестирование слотов осуществляется через передачу slots
при монтировании:
const wrapper = mount(RecursiveTree, {
props: { items },
slots: {
node: '<span class="custom-node">{{ props.item.name }}</span>'
}
})
expect(wrapper.find('.custom-node').text()).toBe('Root')
Это позволяет проверять кастомизированное отображение узлов без изменения внутренней логики компонента.
Тестирование рекурсивных компонентов требует:
shallowMount или
stubs.find и
findAll.trigger и
emitted.nextTick.Такой комплексный подход обеспечивает надежность и предсказуемость поведения рекурсивных компонентов в реальных приложениях.