Простой тест компонента во Vue решает конкретную задачу: проверить, что компонент корректно рендерится, реагирует на входные данные и вызывает ожидаемое поведение при взаимодействии. Vue Test Utils предоставляет минимальный, но достаточный набор инструментов для изоляции компонента и управления его состоянием.
Структура теста строится вокруг трёх опорных элементов:
Тесты располагаются рядом с компонентами или в отдельной директории
tests. На практике чаще используется структура:
components/
Button.vue
tests/
Button.spec.js
Файл теста почти всегда имеет суффикс .spec.js или
.test.js. Это соглашение используется тестовыми раннерами
(Jest, Vitest) для автоматического обнаружения тестов.
Минимальный тест начинается с импорта функций Vue Test Utils и тестируемого компонента:
import { mount } from '@vue/test-utils'
import Button from '@/components/Button.vue'
mount — полностью монтирует компонент со всеми
дочерними компонентами.Дополнительно может импортироваться тестовый раннер:
import { describe, it, expect } from 'vitest'
Каждый файл теста организуется вокруг блока
describe:
describe('Button.vue', () => {
// тесты
})
describe:
Название блока обычно совпадает с именем компонента.
Простейший тест проверяет, что компонент вообще монтируется:
it('рендерится без ошибок', () => {
const wrapper = mount(Button)
expect(wrapper.exists()).toBe(true)
})
Структура теста всегда одинакова:
wrapper.expect.wrapper — объект-обёртка над экземпляром компонента.
Через него осуществляется весь контроль:
Пример базовых возможностей:
wrapper.text()
wrapper.html()
wrapper.find('button')
wrapper.classes()
wrapper создаётся на каждый тест, чтобы
исключить утечки состояния.
Большинство компонентов зависят от входных данных. Props передаются при монтировании:
const wrapper = mount(Button, {
props: {
label: 'Отправить'
}
})
Проверка результата:
expect(wrapper.text()).toContain('Отправить')
Изменение props после монтирования:
await wrapper.setProps({ label: 'Сохранить' })
Асинхронность обязательна, так как Vue обновляет DOM не мгновенно.
Vue Test Utils предоставляет методы поиска:
wrapper.find('button')
wrapper.find('.primary')
wrapper.find('[data-test="submit"]')
Корректный подход — использовать data-test атрибуты:
<button data-test="submit">OK</button>
wrapper.find('[data-test="submit"]')
Такой способ не зависит от верстки и классов.
Типичные утверждения для DOM:
expect(wrapper.text()).toBe('OK')
expect(wrapper.find('button').exists()).toBe(true)
expect(wrapper.findAll('li')).toHaveLength(3)
Для классов:
expect(wrapper.classes()).toContain('active')
Для атрибутов:
expect(wrapper.attributes('disabled')).toBeDefined()
События инициируются через trigger:
await wrapper.find('button').trigger('click')
После этого проверяется результат:
Проверка эмита:
expect(wrapper.emitted()).toHaveProperty('click')
Или с аргументами:
expect(wrapper.emitted('submit')[0]).toEqual([payload])
Vue обновляет DOM асинхронно. Любое действие, вызывающее реактивное
изменение, требует await:
await wrapper.trigger('click')
await wrapper.setProps(...)
await wrapper.setValue('text')
Без await тест может завершиться до применения
изменений.
Один тест — одна проверка. Даже простой компонент тестируется несколькими тестами:
it('отображает текст из props', ...)
it('эмитит событие при клике', ...)
it('имеет disabled состояние', ...)
Это упрощает отладку и делает отчёты тестов однозначными.
Собранная структура в чистом виде:
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Button from '@/components/Button.vue'
describe('Button.vue', () => {
it('отображает переданный текст', () => {
const wrapper = mount(Button, {
props: {
label: 'Нажать'
}
})
expect(wrapper.text()).toBe('Нажать')
})
})
Этот шаблон повторяется в большинстве тестов и служит фундаментом для более сложных сценариев: моков, слотов, сторонов состояния и асинхронной логики.