Skip to main content
Technology & EngineeringMigration Patterns216 lines

Jest to Vitest

Migrate a test suite from Jest to Vitest for faster execution and native ESM support

Quick Summary29 lines
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 lines
Paste into your CLAUDE.md or agent config

Jest 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

  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.

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: 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.

Common Pitfalls

  • 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.
  • Timer mocking differencesvi.useFakeTimers() replaces Date, setTimeout, setInterval, and performance.now by default. Pass options to limit what gets faked if tests break.
  • Module mock hoistingvi.mock() is hoisted to the top of the file automatically (same as jest.mock()), but vi.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 v8 or istanbul for coverage. If migrating from Jest's default coverage, install @vitest/coverage-v8 and 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

Get CLI access →