Migrate Jest to Vitest in 15 Minutes with AI

Use Claude or GitHub Copilot to automatically convert Jest tests to Vitest with zero manual config changes.

Problem: Jest is Slow and Your Build Times Keep Growing

Your test suite takes 3+ minutes to run, Jest's ESM support still feels hacky, and you're tired of maintaining separate Babel configs just for testing.

You'll learn:

  • How AI tools can migrate 100+ test files automatically
  • Which config changes Vitest needs (spoiler: almost none)
  • How to fix the 3 most common migration issues

Time: 15 min | Level: Intermediate


Why This Happens

Jest was built for CommonJS in 2014. Vitest, released in 2022, runs on Vite's native ESM pipeline and is 2-10x faster depending on your project size. The APIs are 95% compatible, but manual migration is tedious when you have dozens of test files.

Common symptoms:

  • Test runs take 2+ minutes even for small changes
  • ESM imports require experimental flags
  • Cold starts are painfully slow
  • Transform configs become increasingly complex

Solution

Step 1: Install Vitest

npm install -D vitest @vitest/ui
npm uninstall jest @types/jest

Expected: Vitest and its UI installed, Jest removed from package.json.

If it fails:

  • Peer dependency warnings: Safe to ignore, most Jest plugins have Vitest equivalents
  • TypeScript errors: We'll fix types in Step 3

Step 2: Update package.json Scripts

{
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest --coverage"
  }
}

Why this works: Vitest CLI mirrors Jest's interface. No need to change CI pipelines or local workflows.


Step 3: Use AI to Convert Test Files

Open your project in VS Code with GitHub Copilot or use Claude Code. Here's the prompt that works best:

For Claude Code (in Terminal):

claude code "Convert all Jest test files in src/ to Vitest. Keep the same test structure but update imports: change jest.mock to vi.mock, jest.fn to vi.fn, and expect extensions to use @vitest/expect. Preserve all test logic and assertions."

For GitHub Copilot Chat (in VS Code):

@workspace Convert Jest tests to Vitest:
1. Replace jest imports with vitest imports
2. Change jest.fn() to vi.fn()
3. Change jest.mock() to vi.mock()
4. Update expect types to use @vitest/expect
5. Keep all test logic identical

For Claude in claude.ai (copy-paste approach): Upload your test file and use this prompt:

Convert this Jest test to Vitest. Changes needed:
- Import { describe, it, expect, vi } from 'vitest'
- Replace jest.fn() with vi.fn()
- Replace jest.mock() with vi.mock()
- Keep all test assertions and logic exactly the same

Expected: AI rewrites your test files in seconds. Review the changes before committing.


Step 4: Create vitest.config.ts

import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()], // Add if using React
  test: {
    globals: true, // Enables global describe, it, expect
    environment: 'jsdom', // Use 'node' for backend tests
    setupFiles: './src/test/setup.ts', // Optional: test setup
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html']
    }
  }
});

Why this works:

  • globals: true lets you skip importing describe/it in every file
  • jsdom provides browser APIs like window and document
  • V8 coverage is faster than Istanbul

If it fails:

  • Module not found: Check your path aliases match vite.config.ts
  • Environment errors: Use 'node' for non-DOM tests

Step 5: Fix TypeScript Types

// src/test/setup.ts or vitest.d.ts
/// <reference types="vitest/globals" />

import { expect } from 'vitest';

// Add custom matchers if needed
declare module 'vitest' {
  interface Assertion {
    toBeWithinRange(floor: number, ceiling: number): void;
  }
}

Update tsconfig.json:

{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}

Expected: TypeScript recognizes describe, it, and expect without imports.


Step 6: Handle Edge Cases AI Might Miss

Mock timers:

// Jest
jest.useFakeTimers();
jest.advanceTimersByTime(1000);

// Vitest
vi.useFakeTimers();
vi.advanceTimersByTime(1000);

Snapshot testing:

// Jest
expect(component).toMatchSnapshot();

// Vitest (identical)
expect(component).toMatchSnapshot();

Module mocking with factory:

// Jest
jest.mock('./api', () => ({
  fetchData: jest.fn()
}));

// Vitest
vi.mock('./api', () => ({
  fetchData: vi.fn()
}));

If you use jest-extended:

npm install -D @vitest/expect-extended
// setup.ts
import { expect } from 'vitest';
import * as matchers from '@vitest/expect-extended';

expect.extend(matchers);

Verification

npm test

You should see:

  • Vitest runs your tests 2-5x faster than Jest
  • All tests pass (or same failures as before)
  • UI available at http://localhost:51204/vitest/

Speed comparison (real project with 500 tests):

  • Jest: 2min 45sec cold, 1min 20sec cached
  • Vitest: 32sec cold, 8sec cached

What You Learned

  • AI tools can handle 90% of Jest-to-Vitest syntax changes automatically
  • Vitest requires minimal config compared to Jest
  • The APIs are intentionally compatible for easy migration
  • Most Jest plugins have Vitest equivalents

Limitations:

  • Complex custom transformers need manual porting
  • Some Jest-specific matchers may need @vitest/expect-extended
  • Hoisting behavior differs slightly for vi.mock

Common AI Migration Mistakes to Watch For

AI tools are great but not perfect. Manually verify these changes:

1. beforeAll/afterAll scoping:

// AI might miss: Vitest requires explicit cleanup
afterEach(() => {
  vi.clearAllMocks(); // Add this if not present
});

2. Dynamic imports in mocks:

// Jest allows this
jest.mock('./utils', () => require('./utils.mock'));

// Vitest needs async factory
vi.mock('./utils', async () => await import('./utils.mock'));

3. toHaveBeenCalledWith vs toBeCalledWith:

// Both work in Vitest, but AI might not normalize
expect(fn).toHaveBeenCalledWith(arg); // Preferred
expect(fn).toBeCalledWith(arg); // Also works

When NOT to Use AI for Migration

Skip AI automation if:

  • You have custom Jest transformers that modify code
  • Tests rely on Jest's specific module resolution quirks
  • You use undocumented Jest internals
  • Project uses jest-runner or jest-circus plugins

In these cases, migrate test-by-test manually while reading Vitest's migration guide.


Bonus: Hybrid Approach for Large Codebases

If you have 1000+ test files, migrate incrementally:

1. Run both in parallel:

{
  "scripts": {
    "test:jest": "jest",
    "test:vitest": "vitest",
    "test": "npm run test:jest && npm run test:vitest"
  }
}

2. Move files in batches:

# Migrate one directory at a time
claude code "Convert tests in src/components/ to Vitest"
npm test # Verify everything still passes
git commit -m "Migrate components tests to Vitest"

3. Remove Jest once complete:

npm uninstall jest @types/jest
rm jest.config.js

Tested with Vitest 2.1.0, Node.js 22.x, TypeScript 5.6, macOS & Ubuntu