В тестировании компонентов Vue критически важно отличать проверку внутреннего состояния от проверки поведения. Vue Test Utils предоставляет инструменты, которые позволяют сосредоточиться на том, что компонент делает, а не на том, как он это реализует внутри. Такой подход делает тесты более устойчивыми к изменениям реализации и повышает их читаемость.
Изоляция от реализации Проверка поведения
подразумевает, что тесты не должны зависеть от внутренней структуры
компонента: приватных методов, локального состояния (data)
или деталей жизненного цикла. Вместо этого внимание уделяется:
Сценарии пользователя Тестирование строится вокруг того, как компонент ведёт себя снаружи, например:
Минимизация связей с внутренними данными Прямой
доступ к wrapper.vm.$data или приватным методам считается
плохой практикой, так как изменения внутри компонента не должны ломать
тесты.
Одной из основных задач является проверка реакции компонента на пользовательские действия.
import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
test('увеличивает счетчик при клике', async () => {
const wrapper = mount(Counter)
await wrapper.find('button.increment').trigger('click')
expect(wrapper.text()).toContain('1')
})
Объяснение:
trigger('click') имитирует событие пользователя.wrapper.text() подтверждает
видимое поведение, а не внутреннюю переменную
count.Компоненты часто взаимодействуют через кастомные события. В Vue Test
Utils это проверяется через emitted():
test('отправляет событие submit при отправке формы', async () => {
const wrapper = mount(FormComponent)
await wrapper.find('form').trigger('submit.prevent')
expect(wrapper.emitted()).toHaveProperty('submit')
expect(wrapper.emitted('submit')[0]).toEqual([{ username: 'test' }])
})
Ключевой момент: тест проверяет факт эмита события и его полезную нагрузку, не заглядывая внутрь метода, который это событие генерирует.
Иногда компонент меняет внешний вид в зависимости от props. Проверка поведения фокусируется на видимых изменениях.
test('отображает сообщение об ошибке при ошибочном prop', () => {
const wrapper = mount(AlertComponent, {
props: { type: 'error', message: 'Ошибка!' }
})
expect(wrapper.classes()).toContain('alert-error')
expect(wrapper.text()).toBe('Ошибка!')
})
Здесь нет необходимости проверять data или computed —
тест подтверждает, что поведение DOM соответствует
props.
| Тестирование состояния | Тестирование поведения |
|---|---|
Проверка wrapper.vm.count |
Проверка, что при клике кнопки текст увеличился |
| Проверка приватных методов | Проверка эмита события или видимого эффекта |
| Хрупкость при рефакторинге | Устойчивость к изменениям реализации |
При проверке поведения взаимодействие с дочерними компонентами лучше имитировать через stubs или проверку эмитированных событий:
const wrapper = mount(ParentComponent, {
global: {
stubs: ['ChildComponent']
}
})
await wrapper.findComponent({ name: 'ChildComponent' }).vm.$emit('custom-event')
expect(wrapper.emitted('child-event')).toBeTruthy()
Тест фиксирует результат взаимодействия, а не внутренние методы дочернего компонента.
Для асинхронных действий, таких как запросы API или таймеры, проверка должна фиксировать результат, а не промежуточные состояния:
test('показывает данные после загрузки', async () => {
const wrapper = mount(DataLoader, { props: { url: '/api/data' } })
await wrapper.vm.$nextTick()
expect(wrapper.text()).toContain('Данные загружены')
})
await wrapper.vm.$nextTick() обеспечивает завершение
реактивного обновления DOM.data.items.data.Такой подход превращает Vue Test Utils в инструмент для проверки пользовательского опыта и корректного взаимодействия компонентов, а не просто в средство инспекции состояния.