Migration guide
Jest → Vitest, step by step.
Vitest is close to a drop-in for Jest. Same describe/test/expect surface, but native ESM, 5–10× faster startup, and it runs on your Vite config. Most of the work is mechanical: swap the runner, turn jest.* into vi.*, then chase down a handful of behavioural differences that bite. Here's every step.
Refactyl doesn't migrate Jest to Vitest yet. This guide covers the manual path; the waitlist notifies you when the verified engine ships.
Why this migration is hard to get right
Jest globals aren't globals in Vitest by default
Jest injects describe, test, expect, vi globally without imports. Vitest requires explicit imports unless you set globals: true in vitest.config.ts, and even then the vi object (the Jest mock equivalent) needs importing. A naive runner swap surfaces hundreds of 'describe is not defined' errors.
ESM vs CJS transform differences
Jest runs on Node.js CommonJS via Babel or ts-jest, giving it a legacy transform path that handles dynamic require and module.exports. Vitest runs native ESM, so files that worked with jest.mock() hoisting may fail because ESM module evaluation order differs from CJS. Import order bugs that were hidden surface.
Mock API subtle differences
jest.fn(), jest.mock(), jest.spyOn() map to vi.fn(), vi.mock(), vi.spyOn(), but not identically. jest.mock() auto-mocking behaviour, mockReturnValue chaining, and module factory functions have edge cases where the Vitest equivalent behaves differently. Each needs testing.
How to migrate manually
Install Vitest, remove Jest
Install @vitest/ui @vitest/coverage-v8 (or istanbul), remove jest, babel-jest, @types/jest, jest-environment-jsdom. Add vitest to devDependencies.
Create vitest.config.ts
Configure the test runner: environment (jsdom for React, node for server code), include pattern, coverage thresholds. If you had a jest.config.js, most options translate directly.
Update package.json scripts
Replace "jest" → "vitest run" in your test script. "jest --watch" → "vitest". "jest --coverage" → "vitest run --coverage". If you use a CI flag like --ci, add --reporter=verbose.
Add globals: true OR add explicit imports
Either set globals: true in vitest.config.ts (closest to Jest behaviour) or add `import { describe, test, expect, vi } from 'vitest'` to each test file. The latter is more explicit and avoids IDE type confusion.
Replace jest.* with vi.*
Global search-and-replace: jest.fn() → vi.fn(), jest.mock() → vi.mock(), jest.spyOn() → vi.spyOn(), jest.useFakeTimers() → vi.useFakeTimers(), jest.clearAllMocks() → vi.clearAllMocks(), jest.resetModules() → vi.resetModules(). Most are 1:1.
Run vitest run and fix ESM issues
Run the suite. The most common remaining failures are dynamic require() calls (convert to import()), __dirname/__filename references (replace with fileURLToPath(import.meta.url)), and jest.config.js options that don't exist in Vitest (remove moduleNameMapper if you've switched to Vite path aliases).
What Refactyl's verified version will do
Roadmap
Refactyl doesn't run this migration yet. When it ships, the bar will be the same as every other Refactyl path: every file gated by a real vitest run run, repaired until clean, or clearly flagged with the reason. No unverified files ship.
The public benchmark methodology is at refactyl.com/benchmark . The same dual-metric approach will apply here.
Manual / DIY vs Refactyl (roadmap)
This table describes what Refactyl's Jest→Vitest engine will do. It isn't live yet.
| Concern | Manual / DIY | Refactyl (roadmap) |
|---|---|---|
| Test startup time | Jest cold-starts in 2–5s for moderate suites | Vitest with native ESM and caching: sub-second on warm runs |
| Global mock replacement | Manual regex + file-by-file jest.* → vi.* substitution | Will replace every jest.fn / mock / spy / timer call in batch, once live |
| ESM compatibility fixes | require() and __dirname errors surface one-by-one at runtime | Will be detected and converted in the same pass as the runner migration |
| vitest.config.ts generation | Written by hand from the Vitest docs | Will be generated from your existing jest.config.js, preserving test patterns and thresholds |
| Verification gate | You find out it worked when CI passes | A passing vitest run will be required before delivery |
FAQ
Jest → Vitest migration: common questions
Real questions engineers ask before migrating Jest to Vitest, answered honestly.
On the roadmap
Want Refactyl to do this for you?
Join the waitlist and we'll notify you when the Jest→Vitest engine ships. The bar is the same as every other Refactyl path: every file gated by a real vitest run run before handoff.
Refactyl doesn't migrate Jest to Vitest yet. No misleading trial, no fake demo.
Other migrations