Jest to Vitest
Migrate a test suite from Jest to Vitest for faster execution and native ESM support
You are an expert in migrating test suites from Jest to Vitest for faster test execution, native ESM support, and seamless Vite integration. ## Key Points 1. **Install Vitest** — add Vitest and remove Jest dependencies. 2. **Create vitest.config.ts** — translate Jest configuration (transforms, module mapping, setup files). 3. **Update Imports** — replace `jest` globals with Vitest equivalents (or use globals mode). 4. **Migrate Mocks** — convert `jest.mock()` to `vi.mock()` and `jest.fn()` to `vi.fn()`. 5. **Run and Fix** — execute the test suite, fix failing tests, then remove Jest. - Enable `globals: true` in Vitest config for a near-zero-change migration from Jest. - Run Vitest in watch mode during development (`vitest` with no flags) — it re-runs only affected tests. - Share your `vite.config.ts` aliases and plugins with Vitest by extending it rather than duplicating. - Use `vi.hoisted()` for mock factory functions that need to be hoisted above imports. - Migrate tests incrementally — Vitest can coexist with Jest briefly if you configure different test patterns. - **`jest` global not found** — replace all `jest.*` calls with `vi.*`. A global find-and-replace handles most cases. - **`@types/jest` conflicts** — remove `@types/jest` from dependencies and add `vitest/globals` to TypeScript types. Having both causes type conflicts. ## Quick Example ```bash npm install --save-dev vitest @vitest/coverage-v8 npm uninstall jest ts-jest babel-jest @types/jest jest-environment-jsdom ``` ```bash npm install --save-dev jsdom @testing-library/jest-dom ```
skilldb get migration-patterns-skills/Jest to VitestFull skill: 216 linesJest to Vitest — Migration Patterns
You are an expert in migrating test suites from Jest to Vitest for faster test execution, native ESM support, and seamless Vite integration.
Core Philosophy
Overview
Vitest is a Vite-native testing framework with a Jest-compatible API. Most Jest tests run in Vitest with minimal changes. The primary benefits are significantly faster execution (thanks to esbuild and native ESM), first-class TypeScript support without transpilation config, and shared configuration with your Vite build setup.
Migration Strategy
- Install Vitest — add Vitest and remove Jest dependencies.
- Create vitest.config.ts — translate Jest configuration (transforms, module mapping, setup files).
- Update Imports — replace
jestglobals with Vitest equivalents (or use globals mode). - Migrate Mocks — convert
jest.mock()tovi.mock()andjest.fn()tovi.fn(). - Run and Fix — execute the test suite, fix failing tests, then remove Jest.
Step-by-Step Guide
1. Install Vitest
npm install --save-dev vitest @vitest/coverage-v8
npm uninstall jest ts-jest babel-jest @types/jest jest-environment-jsdom
For DOM testing:
npm install --save-dev jsdom @testing-library/jest-dom
2. Create vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
globals: true, // enables describe/it/expect without imports
environment: 'jsdom', // replaces jest-environment-jsdom
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{ts,tsx,js,jsx}'],
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
include: ['src/**/*.{ts,tsx}'],
exclude: ['src/**/*.test.*', 'src/test/**'],
},
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
});
3. Update the setup file
// src/test/setup.ts
import '@testing-library/jest-dom/vitest'; // changed from jest-dom
// If you had jest.setTimeout, replace with:
// import { vi } from 'vitest';
// vi.setConfig({ testTimeout: 10000 });
4. Update test imports (if not using globals)
// Before — Jest (globals, no import needed)
describe('Calculator', () => {
it('adds numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
// After — Vitest with explicit imports (if globals: false)
import { describe, it, expect } from 'vitest';
describe('Calculator', () => {
it('adds numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
// After — Vitest with globals: true (no import needed, same as Jest)
describe('Calculator', () => {
it('adds numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
5. Migrate mocks
// Before — Jest
jest.mock('./api');
const mockFetch = jest.fn();
jest.spyOn(console, 'error').mockImplementation(() => {});
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
// After — Vitest
vi.mock('./api');
const mockFetch = vi.fn();
vi.spyOn(console, 'error').mockImplementation(() => {});
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
6. Migrate module mocks
// Before — Jest
jest.mock('./database', () => ({
getUser: jest.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
}));
// After — Vitest
vi.mock('./database', () => ({
getUser: vi.fn().mockResolvedValue({ id: '1', name: 'Alice' }),
}));
7. Migrate manual mocks (__mocks__ directory)
Vitest supports __mocks__ directories the same way Jest does. No changes needed for file-based manual mocks.
8. Migrate snapshot tests
Vitest uses the same snapshot format. Existing .snap files are compatible. However, you should delete and regenerate them to avoid false mismatches:
rm -rf src/**/__snapshots__
npx vitest run --update
9. Update TypeScript config
// tsconfig.json — add Vitest types
{
"compilerOptions": {
"types": ["vitest/globals"] // replaces "@types/jest"
}
}
10. Update package.json scripts
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}
Automated find-and-replace
For large codebases, use a quick search-and-replace to handle the bulk of the migration:
# Replace jest.fn/mock/spyOn with vi equivalents
find src -name '*.test.*' -exec sed -i 's/jest\.fn/vi.fn/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.mock/vi.mock/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.spyOn/vi.spyOn/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.useFakeTimers/vi.useFakeTimers/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.useRealTimers/vi.useRealTimers/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.advanceTimersByTime/vi.advanceTimersByTime/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.clearAllMocks/vi.clearAllMocks/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.resetAllMocks/vi.resetAllMocks/g' {} +
find src -name '*.test.*' -exec sed -i 's/jest\.restoreAllMocks/vi.restoreAllMocks/g' {} +
Best Practices
- Enable
globals: truein Vitest config for a near-zero-change migration from Jest. - Run Vitest in watch mode during development (
vitestwith no flags) — it re-runs only affected tests. - Share your
vite.config.tsaliases and plugins with Vitest by extending it rather than duplicating. - Use
vi.hoisted()for mock factory functions that need to be hoisted above imports. - Migrate tests incrementally — Vitest can coexist with Jest briefly if you configure different test patterns.
Common Pitfalls
jestglobal not found — replace alljest.*calls withvi.*. A global find-and-replace handles most cases.@types/jestconflicts — remove@types/jestfrom dependencies and addvitest/globalsto TypeScript types. Having both causes type conflicts.- Timer mocking differences —
vi.useFakeTimers()replacesDate,setTimeout,setInterval, andperformance.nowby default. Pass options to limit what gets faked if tests break. - Module mock hoisting —
vi.mock()is hoisted to the top of the file automatically (same asjest.mock()), butvi.hoisted()must be used if the mock factory references variables declared in the test file. - ESM-only packages — Jest struggles with ESM packages; Vitest handles them natively. You may find that workarounds you had for Jest (like
transformIgnorePatterns) can be removed. - Coverage provider — Vitest uses
v8oristanbulfor coverage. If migrating from Jest's default coverage, install@vitest/coverage-v8and configure the provider explicitly.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add migration-patterns-skills
Related Skills
Class to Functional React
Convert React class components to functional components with hooks
Cra to Nextjs
Migrate a Create React App project to Next.js for server-side rendering and file-based routing
Javascript to Typescript
Migrate a JavaScript codebase to TypeScript incrementally with minimal disruption
Monolith to Microservices
Decompose a monolithic application into microservices using the strangler fig pattern
REST to GRAPHQL
Migrate a REST API to GraphQL while maintaining backward compatibility
SQL to Nosql
Migrate from a relational SQL database to a NoSQL document or key-value store