Зачем тестировать Vue-компоненты
▸Преимущества тестирования
Тесты ловят баги до попадания в продакшн, позволяют безопасно рефакторить код и документируют поведение компонентов.
Настройка Vitest
▸Установка
1npm install -D vitest @vue/test-utils jsdom @vitest/coverage-v8
▸Конфигурация vite.config.ts
1import { defineConfig } from 'vite';2import vue from '@vitejs/plugin-vue';34export default defineConfig({5 plugins: [vue()],6 test: {7 globals: true,8 environment: 'jsdom',9 },10});
▸Package.json
1{2 "scripts": {3 "test": "vitest",4 "test:coverage": "vitest run --coverage"5 }6}
Первый тест
▸Тест компонента
1import { describe, it, expect } from 'vitest';2import { mount } from '@vue/test-utils';3import MyButton from './MyButton.vue';45describe('MyButton', () => {6 it('renders button with text', () => {7 const wrapper = mount(MyButton, {8 props: { label: 'Нажми меня' },9 });10 expect(wrapper.text()).toContain('Нажми меня');11 });12});
▸Тест клика
1it('emits click event', async () => {2 const wrapper = mount(MyButton);3 await wrapper.find('button').trigger('click');4 expect(wrapper.emitted('click')).toHaveLength(1);5});
Методы поиска элементов
▸find и findAll
1const button = wrapper.find('button');2const items = wrapper.findAll('li');
▸findComponent
1const child = wrapper.findComponent(ChildComponent);2expect(child.exists()).toBe(true);
▸text и html
1expect(wrapper.text()).toContain('Hello');2expect(wrapper.html()).toContain('<button>');
Тестирование событий
▸Тест emit
1it('emits input with value', async () => {2 const wrapper = mount(MyInput);3 await wrapper.find('input').setValue('test');4 expect(wrapper.emitted('input')[0]).toEqual(['test']);5});
▸Тест props
1it('passes props correctly', () => {2 const wrapper = mount(MyComponent, {3 props: { title: 'Заголовок' },4 });5 expect(wrapper.props('title')).toBe('Заголовок');6});
Тестирование слотов
1it('renders slot content', () => {2 const wrapper = mount(MyCard, {3 slots: {4 default: '<p>Содержимое слота</p>',5 },6 });7 expect(wrapper.html()).toContain('Содержимое слота');8});
Тестирование асинхронного кода
▸flushPromises
1import { flushPromises } from '@vue/test-utils';23it('loads data', async () => {4 const wrapper = mount(DataComponent);5 await flushPromises();6 expect(wrapper.text()).toContain('Данные загружены');7});
▸nextTick
1import { nextTick } from 'vue';23it('updates DOM', async () => {4 const wrapper = mount(Counter);5 await wrapper.find('button').trigger('click');6 await nextTick();7 expect(wrapper.text()).toContain('1');8});
Мокирование
▸Мок API
1import { vi } from 'vitest';23vi.mock('./api', () => ({4 fetchUser: vi.fn(() => Promise.resolve({ name: 'Иван' })),5}));
▸Мок router
1vi.mock('vue-router', () => ({2 useRouter: () => ({3 push: vi.fn(),4 replace: vi.fn(),5 }),6 useRoute: () => ({7 params: { id: '1' },8 }),9}));
Тестирование Pinia stores
1import { setActivePinia, createPinia } from 'pinia';2import { useCounterStore } from './counter';34beforeEach(() => {5 setActivePinia(createPinia());6});78it('increments counter', () => {9 const store = useCounterStore();10 store.increment();11 expect(store.count).toBe(1);12});
Snapshot тесты
1it('matches snapshot', () => {2 const wrapper = mount(MyComponent, {3 props: { title: 'Тест' },4 });5 expect(wrapper.html()).toMatchSnapshot();6});
Покрытие кода
▸Запуск с покрытием
1npm run test:coverage
▸Конфигурация
1// vite.config.ts2export default defineConfig({3 test: {4 coverage: {5 provider: 'v8',6 reporter: ['text', 'json', 'html'],7 include: ['src/**/*.vue', 'src/**/*.ts'],8 },9 },10});
Best Practices
▸Тестируйте поведение, а не реализацию
Не тестируйте внутреннее состояние компонента. Тестируйте что компонент делает с точки зрения пользователя.
▸Используйте data-testid
1<template>2 <button data-testid="submit-button">Отправить</button>3</template>
1const button = wrapper.find('[data-testid="submit-button"]');
▸Избегайте чрезмерного количества snapshot тестов
Snapshot тесты хрупкие. Используйте их для визуальных компонентов, а не для логики.
Заключение
Vitest + Vue Test Utils — стандарт тестирования Vue. Фокусируйтесь на поведении компонентов, используйте правильные методы поиска и мокайте внешние зависимости. Написание тестов делает код надежнее и проще в поддержке.