Тесты: Vitest и Vue Test Utils
Тест — код, который сам проверяет, что компонент работает как задумано: показывает нужный текст, отправляет событие по клику. Vitest — программа запуска тестов, созданная для проектов на Vite. Vue Test Utils — официальная библиотека, которая монтирует компонент в тесте и позволяет кликать по нему и читать его разметку.
Установка
В проекте списка задач todo-app выполните:
npm install -D vitest @vue/test-utils jsdomjsdom имитирует браузер внутри Node.js — компонентам нужен DOM. Укажите его в vite.config.js, в объекте настроек рядом с plugins:
// vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },
},
test: {
environment: 'jsdom',
},
})И добавьте команду в раздел scripts файла package.json: "test:unit": "vitest". Если отметить Vitest при создании проекта, create-vue сделает всё это сам.
Первый тест
Тесты кладут в папку __tests__ рядом с компонентами. Создайте src/components/__tests__/TodoItem.spec.js для компонента TodoItem из урока про события:
// src/components/__tests__/TodoItem.spec.js
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import TodoItem from '../TodoItem.vue'
describe('TodoItem', () => {
it('показывает текст задачи', () => {
const wrapper = mount(TodoItem, {
props: { todo: { id: 1, text: 'Купить хлеб', done: false } },
})
expect(wrapper.text()).toContain('Купить хлеб')
})
})Запустите:
npm run test:unitВ терминале появится строка с галочкой ✓ src/components/__tests__/TodoItem.spec.js (1 test). mount создаёт компонент с переданными props, wrapper.text() возвращает весь его текст. Vitest продолжает следить за файлами и перезапускает тесты при сохранении.
Клики и события
// src/components/__tests__/TodoItem.spec.js
it('сообщает об удалении', async () => {
const wrapper = mount(TodoItem, {
props: { todo: { id: 1, text: 'Купить хлеб', done: false } },
})
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('remove')).toHaveLength(1)
})find ищет элемент по CSS-селектору, trigger вызывает событие. await нужен, чтобы Vue успел обновить разметку. emitted('remove') возвращает список всех вызовов события.
| Метод | Что делает |
|---|---|
wrapper.find('селектор') | Находит элемент |
wrapper.text(), wrapper.html() | Текст и разметка |
await el.trigger('click') | Вызывает событие |
await el.setValue('текст') | Вводит значение в поле |
wrapper.emitted('имя') | Сгенерированные события и их аргументы |
Тест хранилища
Хранилище Pinia проверяют без компонентов — достаточно создать активный экземпляр Pinia:
// src/stores/__tests__/posts.spec.js
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { usePostsStore } from '../posts.js'
describe('posts store', () => {
beforeEach(() => setActivePinia(createPinia()))
it('добавляет статью в начало', () => {
const store = usePostsStore()
store.addPost('Новая', 'Текст')
expect(store.count).toBe(1)
expect(store.posts[0].title).toBe('Новая')
})
})Это тест для проекта blog: Vitest устанавливается в него так же.
Частая ошибка
Проверять результат без await после trigger или setValue. Vue обновляет DOM не сразу, а в следующем такте, и тест прочитает старую разметку — он упадёт, хотя компонент работает правильно. Всегда пишите await перед этими методами и объявляйте функцию теста как async.