ADR-019: Migrate from Jest to Vitest for Testing
Statusโ
Accepted
Dateโ
2024-12-16
Contextโ
Our test suite has 2,585 tests across 96 test files. We're experiencing significant issues with Jest's ESM mocking capabilities:
Current Problemsโ
- 59 test failures in CI - Tests timeout in GitHub Actions that pass locally
jest.unstable_mockModule()limitations - The ESM mocking API is experimental and unreliable:- Requires async top-level imports (
await import()after mock setup) - Mocks don't hoist like CommonJS
jest.mock() - TypeScript type inference struggles with mocked types
- Still marked "unstable" even in Jest 30
- Requires async top-level imports (
- Slow test execution - ESM transformation overhead adds ~2-3x execution time
- Complex workarounds - Tests need explicit
--experimental-vm-modulesflag and special patterns
Failing Test Files (All Timeout Issues)โ
| File | Duration | Failures |
|---|---|---|
deployment-guidance-tool.test.ts | 210s | 12 tests |
troubleshoot-guided-workflow-tool.test.ts | 377s | 15 tests |
review-existing-adrs-tool.test.ts | 120s | 10 tests |
adr-bootstrap-validation-tool.test.ts | 241s | 22 tests |
Alternatives Consideredโ
- Stay with Jest + Increase Timeouts: Doesn't solve the underlying ESM mocking issues
- Jest + Sinon: Better mocking but doesn't solve ESM transformation overhead
- Jest + Dependency Injection: High refactoring effort, same speed issues
- Vitest: Native ESM support, faster execution, Jest-compatible API
Decisionโ
Migrate from Jest to Vitest for all testing.
Why Vitestโ
- Native ESM Support: No experimental flags, no transformation overhead
- 3-5x Faster: Leverages Vite's optimized module handling
- 95% Jest Compatible: Same
describe/it/expectsyntax, similar mocking API - Better TypeScript: Native support without Babel
- Simpler Mocking:
vi.mock()works likejest.mock()should have worked
Migration Syntax Changesโ
// Before (Jest ESM)
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../src/utils/adr-discovery.js', () => ({
discoverAdrsInDirectory: jest.fn(),
}));
const { generateDeploymentGuidance } = await import('../../src/tools/...');
// After (Vitest)
import { vi, describe, it, expect } from 'vitest';
vi.mock('../../src/utils/adr-discovery.js', () => ({
discoverAdrsInDirectory: vi.fn(),
}));
import { generateDeploymentGuidance } from '../../src/tools/...'; // Normal import!
Migration Planโ
Phase 1: Setup (This PR)โ
- Install Vitest and dependencies
- Create
vitest.config.ts - Update
package.jsonscripts - Migrate one test file as proof-of-concept
Phase 2: Core Migrationโ
- Migrate failing test files first (4 files, ~59 tests)
- Migrate tool tests (
tests/tools/) - Migrate utility tests (
tests/utils/)
Phase 3: Complete Migrationโ
- Migrate integration tests
- Migrate performance tests
- Remove Jest dependencies
- Update CI workflows
Phase 4: Cleanupโ
- Remove Jest configuration
- Update documentation
- Archive migration scripts
Consequencesโ
Positiveโ
- Faster test execution (3-5x improvement expected)
- Reliable ESM mocking without experimental flags
- Simpler test setup code
- Better TypeScript integration
- More active development and community support
Negativeโ
- One-time migration effort (~2-4 hours)
- Team needs to learn minor API differences
- Some Jest-specific features may need alternatives
Neutralโ
- Test files need syntax updates (mostly search-replace)
- CI configuration needs updates
- Coverage reporting tools remain compatible