AI News Hub Logo

AI News Hub

Vitest 2.0 vs Jest: We Migrated 400 Tests and Here's What Actually Changed

DEV Community
Atlas Whoff

Vitest 2.0 vs Jest: We migrated a 400-test Jest suite last month. Runtime dropped from 47 seconds to 11 seconds. Here is the honest breakdown. Vitest 1.x was already faster than Jest for TypeScript projects because it reuses Vite's transform pipeline instead of running ts-jest or babel-jest. Vitest 2.0 adds: Browser mode — run tests in Chromium/Firefox/WebKit via Playwright Improved workspace — monorepo-aware with shared configs Coverage v8 integrated without extra configuration Snapshot serializers now match Jest's format exactly (v1 migration blocker is gone) npm uninstall jest @types/jest ts-jest jest-environment-jsdom npm install -D vitest @vitest/coverage-v8 jsdom @testing-library/jest-dom import { defineConfig } from "vitest/config" import react from "@vitejs/plugin-react" import tsconfigPaths from "vite-tsconfig-paths" export default defineConfig({ plugins: [react(), tsconfigPaths()], test: { environment: "jsdom", globals: true, setupFiles: ["./src/test/setup.ts"], coverage: { provider: "v8", reporter: ["text", "html", "lcov"], }, }, }) { "scripts": { "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage", "test:ui": "vitest --ui" } } With globals: true, existing test files need only one change — replace jest.mock/jest.fn/jest.spyOn with vi equivalents: find src -name "*.test.*" -exec sed -i "" "s/jest\.mock/vi.mock/g" {} + find src -name "*.test.*" -exec sed -i "" "s/jest\.fn/vi.fn/g" {} + find src -name "*.test.*" -exec sed -i "" "s/jest\.spyOn/vi.spyOn/g" {} + Vitest hoists vi.mock() statically. Variables from outer scope inside the factory cause ReferenceError: // Breaks: outer variable referenced inside vi.mock factory const mockUser = { id: "1" } vi.mock("./auth", () => ({ getUser: () => mockUser })) // Works: value defined inline vi.mock("./auth", () => ({ getUser: () => ({ id: "1" }) })) vi.useFakeTimers() vi.runAllTimers() vi.useRealTimers() Remove __esModule: true from any mocks — Vitest handles ESM natively and does not need it. Our project: 400 tests, TypeScript, React, service layer with DB mocks. Runner Cold run Watch (incremental) Jest + ts-jest 47s 12s per change Vitest 2.0 11s 1.2s per change The watch mode improvement is the real productivity win. For components that rely on real browser APIs (ResizeObserver, canvas, clipboard): export default defineConfig({ test: { browser: { enabled: true, provider: "playwright", name: "chromium", }, }, }) Run with vitest --browser. Tests execute in real Chromium — slower than jsdom but accurate for UI-heavy components. Large team with Jest expertise and no performance pain Heavy CJS module usage where ESM migration is too costly Relies on Jest-specific custom matcher ecosystem For any greenfield TypeScript + Vite/Next.js project, start with Vitest. Shipping a TypeScript SaaS that needs a solid foundation? The Ship Fast Skill Pack includes testing patterns, Vitest setup, and 20+ production-ready Claude Code skills for $49.