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.
Scanned 2/12/2026
Install via CLI
openskills install jezweb/claude-skills---
name: vitest
description: |
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.
license: MIT
---
# Vitest - Modern Test Framework
**Status**: Production Ready
**Last Updated**: 2026-02-06
**Vitest Version**: 4.x
**Vite Compatibility**: 6.x
---
## Quick Start
```bash
# 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)
```typescript
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
},
});
```
### React Config
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```bash
# Update all snapshots
vitest run --update
# Interactive update
vitest --ui
```
---
## In-Source Testing
Test code directly in source files (tree-shaken in production):
```typescript
// 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:**
```typescript
// vitest.config.ts
export default defineConfig({
test: {
includeSource: ['src/**/*.{js,ts}'],
},
define: {
'import.meta.vitest': 'undefined', // Tree-shake in production
},
});
```
---
## Workspace Configuration (Monorepos)
```typescript
// 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
```typescript
// vitest.config.ts
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright', // or 'webdriverio'
name: 'chromium',
headless: true,
},
},
});
```
```bash
# Install browser provider
pnpm add -D @vitest/browser playwright
```
---
## Coverage
```bash
# Install coverage provider
pnpm add -D @vitest/coverage-v8
# Run with coverage
vitest run --coverage
```
```typescript
// 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
1. **Replace imports:**
```typescript
// Before (Jest)
import { jest } from '@jest/globals';
// After (Vitest)
import { vi } from 'vitest';
```
2. **Update config:**
```typescript
// jest.config.js → vitest.config.ts
export default defineConfig({
test: {
globals: true, // Enables describe/it/expect without imports
environment: 'jsdom',
},
});
```
3. **Replace jest. with vi.:**
```bash
# Quick replace (review changes carefully)
find src -name "*.test.ts" -exec sed -i 's/jest\./vi./g' {} \;
```
---
## Common Patterns
### Testing Async Code
```typescript
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
```typescript
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
```typescript
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
```bash
vitest run -t "test name"
vitest run src/specific.test.ts
```
### Debug Mode
```bash
# With Node inspector
node --inspect-brk ./node_modules/vitest/vitest.mjs run
# Or use Vitest UI
vitest --ui
```
### Watch Mode
```bash
vitest # Watch mode (default)
vitest run # Single run
vitest watch # Explicit watch
```
---
## Troubleshooting
### "Cannot find module" in mocks
```typescript
// 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
```typescript
// vitest.config.ts - for CJS dependencies
export default defineConfig({
test: {
deps: {
inline: ['problematic-cjs-package'],
},
},
});
```
### Globals Not Defined
```typescript
// If using globals: true but TypeScript complains
// Add to tsconfig.json:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
```
---
## See Also
- `testing-patterns` skill - General testing patterns
- `testing-library` skill - React Testing Library integration
- Official docs: https://vitest.dev
No comments yet. Be the first to comment!