Розробка Unit-тестів для фронтенду (Vitest)
Vitest — тест-раннер нового покоління від команди Vite. Використовує ту ж конфігурацію, що й Vite-проект, підтримує нативний ESM, на порядок швидший за Jest на великих проектах. Вибір за замовчуванням для Vite, Nuxt 3, SvelteKit-проектів.
Налаштування
npm install -D vitest @vitest/ui jsdom @testing-library/react @testing-library/jest-dom
// vite.config.ts (або vitest.config.ts)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
thresholds: { lines: 80, functions: 80, branches: 70 },
},
},
});
// src/test/setup.ts
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
afterEach(() => cleanup());
Синтаксис ідентичний Jest
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { formatDate } from '../utils/date';
describe('formatDate', () => {
it('formats ISO date to Ukrainian locale', () => {
const date = new Date('2024-11-15T10:00:00Z');
expect(formatDate(date, 'uk-UA')).toBe('15 листопада 2024 р.');
});
it('returns dash for null', () => {
expect(formatDate(null)).toBe('—');
});
});
Тестування компонентів
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import userEvent from '@testing-library/user-event';
import { SearchInput } from './SearchInput';
describe('SearchInput', () => {
it('calls onSearch after debounce', async () => {
vi.useFakeTimers();
const onSearch = vi.fn();
render(<SearchInput onSearch={onSearch} debounce={300} />);
await userEvent.type(screen.getByRole('searchbox'), 'ноутбук');
expect(onSearch).not.toHaveBeenCalled(); // debounce ще не пройшов
vi.advanceTimersByTime(300);
expect(onSearch).toHaveBeenCalledWith('ноутбук');
vi.useRealTimers();
});
it('clears input on Escape', async () => {
render(<SearchInput onSearch={vi.fn()} />);
const input = screen.getByRole('searchbox');
await userEvent.type(input, 'text');
await userEvent.keyboard('{Escape}');
expect(input).toHaveValue('');
});
});
Моки та шпигуни
// vi.mock — заміна модуля
vi.mock('../services/api', () => ({
getProducts: vi.fn().mockResolvedValue([
{ id: 1, name: 'MacBook', price: 150000 },
]),
}));
// vi.spyOn — шпион за методом
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
Concurrent-тесты (відмінність від Jest)
// Vitest підтримує паралельні тесты всередині describe
describe.concurrent('parallel tests', () => {
it.concurrent('test 1', async () => { ... });
it.concurrent('test 2', async () => { ... });
});
UI-режим
# Інтерактивний браузерний UI
npx vitest --ui
# Відкриває http://localhost:51204/__vitest__/
# Показує дерево тестів, coverage, diff провальних тестів
Performance vs Jest
На проекті з 500+ тестами Vitest зазвичай в 2–5 разів швидший за Jest завдяки:
- Нативному ESM без трансформації в CJS
- Загальному dev-серверу з основним Vite-процесом
- Watch mode з HMR для тестів
Часовий графік
Налаштування Vitest + міграція з Jest (якщо потрібна) + написання першого пакету тестів: 2–4 дні.







