lenec ru

← все посты

Vitest vs Jest в 2026: быстрые тесты для Vite-проектов и не только

16K

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 в моках.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.