Loading...
Loading...
Build fast unit and integration tests with Vitest 4.x. Covers configuration for Workers/React/Node, vi.mock/vi.spyOn patterns, snapshot testing, in-source testing, workspace configuration, and browser mode. Use when: setting up tests, migrating from Jest, mocking modules, testing React components, or configuring monorepo workspaces. Keywords: vitest, test, unit test, vi.mock, vi.spyOn, snapshot, coverage, Jest migration.
npx skill4agent add jezweb/claude-skills vitest# Install
pnpm add -D vitest
# Add to package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
},
});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,
},
});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,
},
});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);
});
});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);
});
});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();
});
});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 all snapshots
vitest run --update
# Interactive update
vitest --ui// 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);
});
});
}// vitest.config.ts
export default defineConfig({
test: {
includeSource: ['src/**/*.{js,ts}'],
},
define: {
'import.meta.vitest': 'undefined', // Tree-shake in production
},
});// 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',
},
},
},
]);// 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# 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 | Vitest |
|---|---|
| |
| |
| |
| |
| |
| |
// Before (Jest)
import { jest } from '@jest/globals';
// After (Vitest)
import { vi } from 'vitest';// jest.config.js → vitest.config.ts
export default defineConfig({
test: {
globals: true, // Enables describe/it/expect without imports
environment: 'jsdom',
},
});# Quick replace (review changes carefully)
find src -name "*.test.ts" -exec sed -i 's/jest\./vi./g' {} \;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');
});
});import { describe, it, expect, beforeEach } from 'vitest';
describe('with fixtures', () => {
let testData: TestData;
beforeEach(() => {
testData = createTestFixture();
});
it('uses fixture', () => {
expect(testData.id).toBeDefined();
});
});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);
});
});vitest run -t "test name"
vitest run src/specific.test.ts# With Node inspector
node --inspect-brk ./node_modules/vitest/vitest.mjs run
# Or use Vitest UI
vitest --uivitest # Watch mode (default)
vitest run # Single run
vitest watch # Explicit watch// 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// vitest.config.ts - for CJS dependencies
export default defineConfig({
test: {
deps: {
inline: ['problematic-cjs-package'],
},
},
});// If using globals: true but TypeScript complains
// Add to tsconfig.json:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}testing-patternstesting-library