Vitest vs Jest в 2026: быстрые тесты для Vite-проектов и не только
Jest доминировал в тестировании JavaScript последние 7 лет. Но с приходом Vite как стандартного сборщика появился Vitest — тест-раннер, который использует ту же конфигурацию и pipeline что и ваш dev-сервер. Результат — тесты запускаются мгновенно, ESM работает из коробки, а API на 95% совместим с Jest.
Vitest: зачем появился и как связан с Vite
Главная проблема Jest в современных проектах — он не понимает ESM нативно. Jest построен на CommonJS, и для работы с TypeScript, JSX, ESM-модулями требует трансформеров (ts-jest, babel-jest, @swc/jest). Каждый трансформер — это конфигурация, совместимость и время на компиляцию.
Vitest использует Vite как трансформер. Если у вас уже есть vite.config.ts — Vitest подхватывает его автоматически: алиасы, плагины, resolve-логика. Один конфиг на dev-сервер, билд и тесты:
// vite.config.ts — единый конфиг
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': './src' },
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
});
API-совместимость с Jest
Vitest намеренно повторяет API Jest. Миграция существующих тестов — часто замена импортов:
// Работает и в Jest, и в Vitest
import { describe, it, expect, vi } from 'vitest';
// В Jest: import { jest } from '@jest/globals'; (или глобалы)
describe('UserService', () => {
it('возвращает пользователя по id', async () => {
const mockFetch = vi.fn().mockResolvedValue({
json: () => ({ id: 1, name: 'Иван' }),
});
const user = await getUser(1, mockFetch);
expect(user).toEqual({ id: 1, name: 'Иван' });
expect(mockFetch).toHaveBeenCalledWith('/api/users/1');
});
it('снапшот', () => {
const result = formatUser({ id: 1, name: 'Иван' });
expect(result).toMatchSnapshot();
});
});
Что совместимо: describe/it/test, expect с матчерами, vi.fn()/vi.mock()/vi.spyOn() (аналоги jest.fn()), beforeEach/afterEach, снапшоты, fake timers. Что отличается: vi вместо jest, некоторые edge-cases в hoisting моков.
Скорость: HMR, worker threads, бенчмарки
Vitest быстрее Jest по двум причинам:
- Нет холодного старта. Vite кэширует трансформации и использует esbuild для TypeScript — первый запуск быстрее, повторные — мгновенные.
- Watch mode с HMR. При изменении файла перезапускаются только затронутые тесты. Vite знает граф зависимостей — не нужно перезапускать весь сьют.
Бенчмарки (React-проект, 500 тестов, M2 MacBook):
Метрика | Vitest 2.x | Jest 30 | Разница
─────────────────────────────────────────────────────────
Cold run | 4.2s | 12.8s | 3x быстрее
Watch (1 файл) | 0.3s | 2.1s | 7x быстрее
Full re-run (warm) | 2.8s | 9.4s | 3.4x быстрее
С coverage (v8) | 5.1s | 18.2s | 3.6x быстрее
Особенно заметна разница в watch mode: Vitest перезапускает только изменённые тесты за доли секунды, Jest часто перезапускает больше необходимого.
Browser mode и component testing
Vitest Browser Mode запускает тесты в реальном браузере (через Playwright или WebDriverIO) вместо jsdom. Это решает проблему: jsdom не реализует layout, CSS, intersection observer и десятки других Web API.
// vitest.config.ts — browser mode
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
});
// Тест компонента в реальном браузере
import { render } from '@testing-library/react';
import { expect, test } from 'vitest';
import { Tooltip } from './Tooltip';
test('тултип появляется при hover', async () => {
const { getByRole, getByText } = render(
<Tooltip text="Подсказка">
<button>Наведи</button>
</Tooltip>
);
await userEvent.hover(getByRole('button'));
expect(getByText('Подсказка')).toBeVisible(); // реальная видимость!
});
В Jest для browser testing нужны отдельные инструменты (Playwright, Cypress CT). Vitest объединяет unit и component тесты в одном раннере.
Когда что выбрать
Vitest — если:
- Проект на Vite (React, Vue, Svelte, SolidJS).
- Используете ESM, TypeScript, path aliases.
- Хотите быстрый watch mode для TDD.
- Начинаете новый проект — нет legacy Jest-тестов.
Jest — если:
- Большой legacy-проект с тысячами Jest-тестов.
- Не используете Vite (webpack, CRA, Next.js с Turbopack).
- Нужна максимальная стабильность и экосистема (jest-extended, testing-library/jest-dom).
- Бэкенд на Node.js без фронтенд-сборщика.
Вывод
Для Vite-проектов Vitest — очевидный выбор: единый конфиг, мгновенный watch, нативный ESM. Для legacy на webpack Jest по-прежнему работает. Миграция с Jest на Vitest обычно занимает день — API почти идентичен, основная работа в настройке конфига и замене jest на vi в моках.