
Vitest Testing
- 85 installs
- 49 repo stars
- Updated August 4, 2026
- laurigates/claude-plugins
Helps with testing & qa tasks.
About
vitest-testing is a Claude Code skill for testing & qa. It helps solo builders move faster with AI-assisted development.
- vitest-testing
- Testing & QA
- AI-coding skill
Vitest Testing by the numbers
- 85 all-time installs (skills.sh)
- +5 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,054 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/laurigates/claude-plugins --skill vitest-testingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 85 |
|---|---|
| repo stars | ★ 49 |
| Last updated | August 4, 2026 |
| Repository | laurigates/claude-plugins ↗ |
What it does
Helps with testing & qa tasks.
Files
Vitest Testing
Vitest is a modern test runner designed for Vite projects. It's fast, ESM-native, and provides a Jest-compatible API with better TypeScript support and instant HMR-powered watch mode.
When to Use This Skill
| Use this skill when... | Use another skill instead when... |
|---|---|
| Setting up or configuring Vitest | Writing E2E browser tests (use playwright-testing) |
| Writing unit/integration tests in TS/JS | Testing Python code (use python-testing) |
| Migrating from Jest to Vitest | Analyzing test quality (use test-quality-analysis) |
| Configuring coverage thresholds | Generating property-based tests (use property-based-testing) |
| Using mocks, spies, or fake timers | Validating test effectiveness (use mutation-testing) |
Core Expertise
- Vite-native: Reuses Vite config, transforms, and plugins
- Fast: Instant feedback with HMR-powered watch mode
- Jest-compatible: Drop-in replacement with similar API
- TypeScript: First-class TypeScript support
- ESM: Native ESM support, no transpilation needed
Installation
bun add --dev vitest
bun add --dev @vitest/coverage-v8 # Coverage (recommended)
bun add --dev happy-dom # DOM testing (optional)
bunx vitest --version # VerifyConfiguration (vitest.config.ts)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
},
});Essential Commands
bunx vitest # Watch mode (default)
bunx vitest run # Run once (CI mode)
bunx vitest --coverage # With coverage
bunx vitest src/utils.test.ts # Specific file
bunx vitest -t "should add numbers" # Filter by name
bunx vitest related src/utils.ts # Related tests
bunx vitest -u # Update snapshots
bunx vitest bench # Benchmarks
bunx vitest --ui # UI modeWriting Tests
Basic Test Structure
import { describe, it, expect } from 'vitest';
import { add, multiply } from './math';
describe('math utils', () => {
it('should add two numbers', () => {
expect(add(2, 3)).toBe(5);
});
it('should multiply two numbers', () => {
expect(multiply(2, 3)).toBe(6);
});
});Key Assertions
| Assertion | Description |
|---|---|
toBe(value) | Strict equality |
toEqual(value) | Deep equality |
toStrictEqual(value) | Deep strict equality |
toBeTruthy() / toBeFalsy() | Truthiness |
toBeNull() / toBeUndefined() | Null checks |
toBeGreaterThan(n) / toBeLessThan(n) | Numeric comparison |
toBeCloseTo(n) | Float comparison |
toMatch(regex) / toContain(str) | String matching |
toHaveLength(n) | Array/string length |
toHaveProperty(key) | Object property |
toMatchObject(obj) | Partial object match |
toThrow(msg) | Error throwing |
Async Tests
test('async test', async () => {
const data = await fetchData();
expect(data).toBe('expected');
});
test('promise resolves', async () => {
await expect(fetchData()).resolves.toBe('expected');
});
test('promise rejects', async () => {
await expect(fetchBadData()).rejects.toThrow('error');
});Mocking (Essential Patterns)
import { vi, test, expect } from 'vitest';
// Mock function
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
// Mock module
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: 'John' })),
}));
// Mock timers
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.restoreAllMocks();
// Spy on method
const spy = vi.spyOn(object, 'method');Snapshot Testing
test('snapshot test', () => {
expect(data).toMatchSnapshot();
});
test('inline snapshot', () => {
expect(result).toMatchInlineSnapshot('5');
});
// Update snapshots: bunx vitest -uCoverage
bun add --dev @vitest/coverage-v8
bunx vitest --coverageKey config options: provider, reporter, include, exclude, thresholds.
Agentic Optimizations
| Context | Command |
|---|---|
| Quick test | bunx vitest --reporter=dot --bail=1 |
| CI test | bunx vitest run --reporter=junit |
| Coverage check | bunx vitest --coverage --reporter=dot |
| Single file | bunx vitest run src/utils.test.ts --reporter=dot |
| Failed only | bunx vitest --changed --bail=1 |
For detailed examples, advanced patterns, and best practices, see REFERENCE.md.
References
- Official docs: https://vitest.dev
- Configuration: https://vitest.dev/config/
- API reference: https://vitest.dev/api/
- Migration from Jest: https://vitest.dev/guide/migration.html
- Coverage: https://vitest.dev/guide/coverage.html
Vitest Testing - Reference
Detailed reference material for Vitest test runner configuration, mocking, migration, and CI/CD integration.
Recommended Production Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import path from 'path';
export default defineConfig({
test: {
globals: true,
environment: 'node', // or 'happy-dom' for browser-like environment
setupFiles: ['./test/setup.ts'],
include: ['**/*.{test,spec}.{js,ts,jsx,tsx}'],
exclude: ['node_modules', 'dist', 'build', '.next'],
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'json', 'html', 'lcov'],
exclude: [
'node_modules/',
'test/',
'**/*.config.{js,ts}',
'**/*.d.ts',
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
testTimeout: 10000,
mockReset: true,
restoreMocks: true,
clearMocks: true,
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});Merging with Vite Config
// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import viteConfig from './vite.config';
export default mergeConfig(
viteConfig,
defineConfig({
test: {
globals: true,
environment: 'happy-dom',
setupFiles: ['./test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
})
);Mocking - Detailed Patterns
Mock Functions
import { vi, test, expect } from 'vitest';
test('mock function', () => {
const mockFn = vi.fn();
mockFn('hello');
mockFn('world');
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith('hello');
expect(mockFn).toHaveBeenLastCalledWith('world');
});
// Mock implementation
test('mock implementation', () => {
const mockFn = vi.fn((x: number) => x * 2);
expect(mockFn(5)).toBe(10);
expect(mockFn).toHaveBeenCalledWith(5);
});
// Mock return values
test('mock return values', () => {
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);
mockFn.mockReturnValueOnce(1).mockReturnValueOnce(2);
expect(mockFn()).toBe(1);
expect(mockFn()).toBe(2);
expect(mockFn()).toBe(42); // Returns default
});Mock Modules
import { vi, beforeEach, test, expect } from 'vitest';
// Mock entire module
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: 'John' })),
createUser: vi.fn(),
}));
import { fetchUser, createUser } from './api';
beforeEach(() => {
vi.clearAllMocks();
});
test('uses mocked api', async () => {
const user = await fetchUser(1);
expect(user).toEqual({ id: 1, name: 'John' });
expect(fetchUser).toHaveBeenCalledWith(1);
});Partial Mocking
import { vi, test, expect } from 'vitest';
// Mock only specific exports
vi.mock('./utils', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
add: vi.fn(() => 999), // Mock only 'add'
};
});
import { add, multiply } from './utils';
test('partial mock', () => {
expect(add(1, 2)).toBe(999); // Mocked
expect(multiply(2, 3)).toBe(6); // Real implementation
});Mock Timers
import { vi, beforeEach, afterEach, test, expect } from 'vitest';
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
test('timer mocking', () => {
const callback = vi.fn();
setTimeout(callback, 1000);
expect(callback).not.toHaveBeenCalled();
vi.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
});
test('fast-forward time', () => {
const callback = vi.fn();
setInterval(callback, 1000);
vi.advanceTimersByTime(3500);
expect(callback).toHaveBeenCalledTimes(3);
});Mock Globals
import { vi, test, expect } from 'vitest';
test('mock fetch', async () => {
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: 'mocked' }),
})
);
const response = await fetch('https://api.example.com');
const data = await response.json();
expect(data).toEqual({ data: 'mocked' });
expect(fetch).toHaveBeenCalledWith('https://api.example.com');
});Coverage - Detailed Configuration
v8 Provider (Recommended)
# Install
bun add --dev @vitest/coverage-v8
# Run with coverage
bunx vitest --coverageConfiguration:
// vitest.config.ts
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
include: ['src/**/*.ts'],
exclude: [
'node_modules/',
'test/',
'**/*.config.ts',
'**/*.d.ts',
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
},
});Istanbul Provider
# Install
bun add --dev @vitest/coverage-istanbul
# Run with coverage
bunx vitest --coverageWhen to use:
- Need specific Istanbul features
- Migrating from Jest (Istanbul is default)
- Note: Slower than v8
Watch Mode - Detailed
# Start watch mode (default)
bunx vitest
# Commands in watch mode:
# - r: rerun all tests
# - f: rerun only failed tests
# - u: update snapshots
# - p: filter by filename
# - t: filter by test name
# - q: quitConfiguration:
export default defineConfig({
test: {
watch: true,
watchExclude: ['node_modules/**', 'dist/**'],
},
});UI Mode
# Start UI mode
bunx vitest --ui
# Opens browser at http://localhost:51204Features:
- Visual test browser
- Click to run specific tests
- View test results and logs
- Filter and search tests
- Inspect coverage
Setup Files
// test/setup.ts
import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
beforeAll(() => {
// Setup once before all tests
console.log('Starting tests');
});
afterAll(() => {
// Cleanup once after all tests
console.log('Tests complete');
});
beforeEach(() => {
// Setup before each test
vi.clearAllMocks();
});
afterEach(() => {
// Cleanup after each test
vi.restoreAllMocks();
});Reference in config:
export default defineConfig({
test: {
setupFiles: ['./test/setup.ts'],
},
});Migration from Jest
API Compatibility
Vitest provides a Jest-compatible API:
// Works in both Jest and Vitest
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('my tests', () => {
beforeEach(() => {
// Setup
});
it('should work', () => {
expect(true).toBe(true);
});
});Migration Steps
1. Replace Jest with Vitest:
bun remove jest @types/jest
bun add --dev vitest2. Update scripts in package.json:
{
"scripts": {
"test": "vitest",
"test:ci": "vitest run",
"test:coverage": "vitest --coverage"
}
}3. Convert jest.config.js to vitest.config.ts:
// jest.config.js -> vitest.config.ts
export default defineConfig({
test: {
globals: true,
environment: 'jsdom', // was testEnvironment in Jest
setupFiles: ['./test/setup.ts'], // was setupFilesAfterEnv
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
});4. Update imports:
// Before (Jest)
import { describe, it, expect } from '@jest/globals';
// After (Vitest)
import { describe, it, expect } from 'vitest';5. Update mocking syntax:
// Jest
jest.mock('./api');
const mockFn = jest.fn();
// Vitest
vi.mock('./api');
const mockFn = vi.fn();CI/CD Integration
GitHub Actions
name: Test
on:
push:
branches: [main]
pull_request:
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests
run: bunx vitest run --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.infoGitLab CI
test:
image: oven/bun:latest
stage: test
script:
- bun install --frozen-lockfile
- bunx vitest run --coverage
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xmlTroubleshooting
Tests Not Running
# Verify config is detected
bunx vitest --config vitest.config.ts
# Check test file patterns
bunx vitest --reporter=verbose
# Debug configuration
bunx vitest --helpESM Import Errors
// vitest.config.ts
export default defineConfig({
test: {
environment: 'node',
globals: true,
},
resolve: {
conditions: ['import', 'node'],
},
});Coverage Thresholds Failing
# View detailed coverage report
bunx vitest --coverage
# Open HTML report
open coverage/index.html
# Adjust thresholds in vitest.config.ts:
coverage: {
thresholds: {
lines: 70, // Lower threshold
functions: 70,
branches: 70,
statements: 70,
},
}Slow Tests
# Run tests in parallel (default)
bunx vitest --threads
# Limit parallelism
bunx vitest --maxWorkers=4
# Profile slow tests
bunx vitest --reporter=verbosePerformance Comparison
| Tool | Startup | Watch Mode | Coverage |
|---|---|---|---|
| Vitest | ~100ms | Instant HMR | v8 (fast) |
| Jest | ~3-5s | Polling | Istanbul (slower) |
Vitest is 5-10x faster than Jest in watch mode.