vitest
90
总安装量
90
周安装量
#2549
全站排名
安装命令
npx skills add https://github.com/jezweb/claude-skills --skill vitest
Agent 安装分布
claude-code
68
opencode
62
gemini-cli
60
codex
55
cursor
52
Skill 文档
Vitest – Modern Test Framework
Status: Production Ready Last Updated: 2026-02-06 Vitest Version: 4.x Vite Compatibility: 6.x
Quick Start
# Install
pnpm add -D vitest
# Add to package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}
Configuration
Minimal Config (vitest.config.ts)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
},
});
React Config
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
css: true,
},
});
Cloudflare Workers Config
import { defineConfig } from 'vitest/config';
import { cloudflare } from '@cloudflare/vite-plugin';
export default defineConfig({
plugins: [cloudflare()],
test: {
globals: true,
environment: 'node',
// Workers tests often need longer timeouts for D1/KV
testTimeout: 10000,
},
});
Mocking Patterns
vi.mock – Module Mocking
import { vi, describe, it, expect } from 'vitest';
import { fetchUser } from './api';
// Mock entire module
vi.mock('./api', () => ({
fetchUser: vi.fn(),
}));
describe('User component', () => {
it('fetches user data', async () => {
// Type-safe mock implementation
vi.mocked(fetchUser).mockResolvedValue({ id: 1, name: 'Test' });
// ... test code
expect(fetchUser).toHaveBeenCalledWith(1);
});
});
vi.spyOn – Spy on Methods
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('Date handling', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01'));
});
afterEach(() => {
vi.useRealTimers();
});
it('uses mocked date', () => {
expect(new Date().getFullYear()).toBe(2026);
});
});
vi.stubGlobal – Global Mocks
import { vi, describe, it, expect } from 'vitest';
describe('Environment', () => {
it('mocks fetch globally', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: 'test' }),
});
vi.stubGlobal('fetch', mockFetch);
const response = await fetch('/api/test');
expect(mockFetch).toHaveBeenCalledWith('/api/test');
vi.unstubAllGlobals();
});
});
Snapshot Testing
Basic Snapshots
import { describe, it, expect } from 'vitest';
describe('Component output', () => {
it('matches snapshot', () => {
const result = renderComponent({ title: 'Hello' });
expect(result).toMatchSnapshot();
});
it('matches inline snapshot', () => {
const result = { name: 'test', count: 42 };
expect(result).toMatchInlineSnapshot(`
{
"count": 42,
"name": "test",
}
`);
});
});
Update Snapshots
# Update all snapshots
vitest run --update
# Interactive update
vitest --ui
In-Source Testing
Test code directly in source files (tree-shaken in production):
// src/utils/math.ts
export function add(a: number, b: number): number {
return a + b;
}
// In-source test block
if (import.meta.vitest) {
const { describe, it, expect } = import.meta.vitest;
describe('add', () => {
it('adds two numbers', () => {
expect(add(1, 2)).toBe(3);
});
});
}
Config for in-source testing:
// vitest.config.ts
export default defineConfig({
test: {
includeSource: ['src/**/*.{js,ts}'],
},
define: {
'import.meta.vitest': 'undefined', // Tree-shake in production
},
});
Workspace Configuration (Monorepos)
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
export default defineWorkspace([
// Each package can have its own config
'packages/*/vitest.config.ts',
// Or define inline
{
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'node',
},
},
{
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
},
]);
Browser Mode Testing
// vitest.config.ts
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright', // or 'webdriverio'
name: 'chromium',
headless: true,
},
},
});
# Install browser provider
pnpm add -D @vitest/browser playwright
Coverage
# Install coverage provider
pnpm add -D @vitest/coverage-v8
# Run with coverage
vitest run --coverage
// vitest.config.ts
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'src/test/',
'**/*.d.ts',
],
thresholds: {
statements: 80,
branches: 80,
functions: 80,
lines: 80,
},
},
},
});
Jest Migration
Key Differences
| Jest | Vitest |
|---|---|
jest.fn() |
vi.fn() |
jest.mock() |
vi.mock() |
jest.spyOn() |
vi.spyOn() |
jest.useFakeTimers() |
vi.useFakeTimers() |
jest.clearAllMocks() |
vi.clearAllMocks() |
@jest/globals |
vitest |
Migration Steps
- Replace imports:
// Before (Jest)
import { jest } from '@jest/globals';
// After (Vitest)
import { vi } from 'vitest';
- Update config:
// jest.config.js â vitest.config.ts
export default defineConfig({
test: {
globals: true, // Enables describe/it/expect without imports
environment: 'jsdom',
},
});
- Replace jest. with vi.:
# Quick replace (review changes carefully)
find src -name "*.test.ts" -exec sed -i 's/jest\./vi./g' {} \;
Common Patterns
Testing Async Code
import { describe, it, expect } from 'vitest';
describe('async operations', () => {
it('resolves promise', async () => {
const result = await fetchData();
expect(result).toBeDefined();
});
it('rejects with error', async () => {
await expect(failingOperation()).rejects.toThrow('Expected error');
});
});
Testing with Fixtures
import { describe, it, expect, beforeEach } from 'vitest';
describe('with fixtures', () => {
let testData: TestData;
beforeEach(() => {
testData = createTestFixture();
});
it('uses fixture', () => {
expect(testData.id).toBeDefined();
});
});
Parameterized Tests
import { describe, it, expect } from 'vitest';
describe.each([
{ input: 1, expected: 2 },
{ input: 2, expected: 4 },
{ input: 3, expected: 6 },
])('double($input)', ({ input, expected }) => {
it(`returns ${expected}`, () => {
expect(double(input)).toBe(expected);
});
});
Debugging
Run Single Test
vitest run -t "test name"
vitest run src/specific.test.ts
Debug Mode
# With Node inspector
node --inspect-brk ./node_modules/vitest/vitest.mjs run
# Or use Vitest UI
vitest --ui
Watch Mode
vitest # Watch mode (default)
vitest run # Single run
vitest watch # Explicit watch
Troubleshooting
“Cannot find module” in mocks
// Ensure mock path matches import path exactly
vi.mock('./api'); // Matches: import { x } from './api'
vi.mock('../api'); // Different! Won't work for './api' imports
ESM/CJS Issues
// vitest.config.ts - for CJS dependencies
export default defineConfig({
test: {
deps: {
inline: ['problematic-cjs-package'],
},
},
});
Globals Not Defined
// If using globals: true but TypeScript complains
// Add to tsconfig.json:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
See Also
testing-patternsskill – General testing patternstesting-libraryskill – React Testing Library integration- Official docs: https://vitest.dev