Jest -> Vitest
Vitest is Jest-API-compatible and runs on Vite's transform pipeline, so most tests move with a config swap and a jest -> vi rename. The sharp edges are vi.mock hoisting and turning on globals.
Last verified · Updated May 22, 2026
Vitest mirrors Jest's API (describe/it/expect, mocks, spies, fake timers) and runs on Vite's esbuild-powered transform, so it handles ESM and TypeScript natively and is faster. Migration is mostly a config move into vite.config.ts plus replacing the jest namespace with vi. Set globals: true to keep Jest-style global test functions.
Key differences
- Config lives in the test field of vite.config.ts, not a separate jest.config.js.
- Use the vi namespace: jest.mock -> vi.mock, jest.fn -> vi.fn, jest.spyOn -> vi.spyOn.
- globals: true restores describe/it/expect without imports.
- environment: 'jsdom' for component/DOM tests (install jsdom).
- Native ESM and TypeScript — no babel-jest transform needed.
Config mapping
| Jest | Vitest |
|---|---|
| jest.config.js / package.json jest | test field in vite.config.ts |
| jest | vi |
| jest.mock | vi.mock (hoisted; use vi.hoisted for refs) |
| jest.fn / jest.spyOn | vi.fn / vi.spyOn |
| jest.useFakeTimers | vi.useFakeTimers |
| testEnvironment: 'jsdom' | environment: 'jsdom' |
| setupFilesAfterEnv | setupFiles |
| injectGlobals (default true) | globals: true (opt in) |
Install and run
# Install Vitest (+ jsdom for component tests)
npm install -D vitest jsdom @testing-library/jest-dom
# Run the suite (non-watch)
npx vitest runvite.config.ts test field
Replaces jest.config.js
import { defineConfig } from 'vite';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
},
});⚠ vi.mock hoists like jest.mock
Vitest hoists vi.mock to the top of the file, so a mock factory that references an outer variable will read it before initialization. Wrap such references in vi.hoisted(() => ...) — this is the most common Jest-to-Vitest migration failure.
AI-assisted migration workflow
Migrate this suite from Jest to Vitest. Install vitest (+ jsdom for component tests), add a test block to vite.config.ts with globals: true and environment: 'jsdom', port setupFiles, then replace every jest.* call with the vi.* equivalent. Check that vi.mock factories hoist correctly — use vi.hoisted where they reference outer variables. Update the test script to 'vitest run' and run the suite after each step.Safety: Keep test behavior identical; only swap the jest API for vi. Pause if a mock needs vi.hoisted.
PR review checklist
- test config moved into vite.config.ts; jest.config.js removed
- globals: true set if tests use describe/it/expect without imports
- All jest.* calls replaced with vi.*
- vi.mock factories that reference outer vars use vi.hoisted
- setupFiles (incl. @testing-library/jest-dom) load correctly
- Suite passes under 'vitest run' with coverage intact
Rollback strategy
- Keep both 'jest' and 'vitest run' scripts until the Vitest suite is fully green.
- Migrate jest -> vi as one commit and config + setupFiles as another.
- Revert the test script to Jest if a mock or timer behavior cannot be reconciled.
Related migration paths
- Webpack vs Vite — Adjacent migration
- Migrate from Webpack to Vite — Adjacent migration
- Fix Vite Build Errors — Related workflow
Official sources
- Vite Guide — vitejs.dev (reliability 98%)
- Vitest — Migrating from Jest — vitest.dev (reliability 98%)
Copy-ready AI prompts
Structured prompts for an AI coding assistant. Inspect first, then execute incrementally, and keep a human in the review loop.
Repo inspection: Repo inspection prompt
You are helping migrate a test suite from Jest to Vitest.
Do not edit files yet. Inspect the repository and report:
1. The Jest config (jest.config.js, package.json jest field, or ts-jest setup) and the test environment (node vs jsdom).
2. Whether tests rely on Jest globals (describe/it/expect without imports) — Vitest needs test.globals: true to match.
3. Every jest.* API in use: jest.mock, jest.fn, jest.spyOn, jest.useFakeTimers — all map to vi.*.
4. setupFiles / setupFilesAfterEnv, including @testing-library/jest-dom usage.
5. moduleNameMapper aliases, transform settings, and any non-ESM deps that Jest handled via babel-jest.
Return: a config-mapping plan (Jest field -> vite.config.ts test field), the list of jest.* calls to rewrite as vi.*, the setupFiles to port, and any timer/mock hoisting risks.Safety: Inspection only. The agent must not modify files in this step.
Works with Claude Code, Cursor, GitHub Copilot.
Migration execution: Migration execution prompt
Migrate this suite from Jest to Vitest, one concern at a time, pausing for review after each.
Order: (1) install vitest and (for component tests) jsdom and @testing-library/jest-dom, (2) add a test block to vite.config.ts with globals: true and environment: 'jsdom', (3) port setupFiles, (4) replace every jest.* call with the vi.* equivalent (jest.mock -> vi.mock, jest.fn -> vi.fn, jest.spyOn -> vi.spyOn, jest.useFakeTimers -> vi.useFakeTimers), (5) verify vi.mock hoisting behaves like jest.mock — factories that reference outer variables need vi.hoisted, (6) update the test script to 'vitest run'.
After each step run the suite and report failures before continuing. Keep the public test behavior identical; do not rewrite assertions beyond the jest-to-vi API swap.Safety: vi.mock is hoisted like jest.mock — do not introduce closures over uninitialized variables. Pause if a mock factory needs vi.hoisted.
Works with Claude Code, Cursor, GitHub Copilot.
Test plan
Commands
npx vitest runnpx vitest run --coveragenpm run build
Manual checks
- Globals: confirm describe/it/expect resolve without imports once globals: true is set.
- Mocks: verify vi.mock factories hoist correctly and produce the same module doubles as jest.mock.
- DOM: confirm jsdom matchers from @testing-library/jest-dom load via setupFiles.
- Timers: re-check any vi.useFakeTimers tests that depended on Jest's default timer behavior.
Regression risks
- jest.mock to vi.mock hoisting differences breaking module mocks.
- Missing globals: true causing 'describe is not defined' errors.
- Tests that depended on Jest-specific timer or fake-module internals.
Acceptance criteria
- The full suite passes under 'vitest run'.
- Coverage matches or exceeds the prior Jest threshold.
- No jest.* references remain in test files.