vitest
Vitest Testing Framework
Collaborating skills
- TDD: skill:
tddfor test-driven development workflow using Vitest - Refactor: skill:
refactorfor improving code with Vitest test coverage - TypeScript: skill:
typescriptfor TypeScript-specific testing patterns and type testing
Vitest is a next-generation testing framework powered by Vite, offering fast tests with native ESM, TypeScript, and JSX support. This skill covers unit testing, component testing, and browser/E2E testing with progressive disclosure.
Quick Reference
Installation
npm install -D vitest
# For browser/E2E testing
npx vitest init browser
Essential Commands
vitest # Run tests in watch mode
vitest run # Run tests once
vitest run --coverage # Run with coverage
vitest --ui # Open Vitest UI
vitest --browser # Run browser tests
Package.json Scripts
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui"
}
}
Verification Commands
npx vitest --version # Check version
npx vitest --help # List all options
Test verification: After vitest run, all tests should pass with green checkmarks. Failed tests show diff output.
File Conventions
| Type | Pattern |
|---|---|
| Test files | *.test.* or *.spec.* |
| Config | vitest.config.ts or vite.config.ts |
| Setup | setup.ts or src/setup-tests.ts |
| Snapshots | __snapshots__/*.snap |
Writing Tests
Basic Test Structure
import { describe, it, expect } from 'vitest'
describe('Math operations', () => {
it('adds two numbers', () => {
expect(1 + 2).toBe(3)
})
it('handles async operations', async () => {
const result = await Promise.resolve(42)
expect(result).toBe(42)
})
})
Test Functions
import { test, it, describe, bench } from 'vitest'
// it and test are aliases
it('simple test', () => {})
// Skip tests
it.skip('skipped test', () => {})
it.skipIf(process.env.CI)('skip in CI', () => {})
// Only run specific tests
it.only('run only this', () => {})
it.todo('implement later')
// Concurrent tests
it.concurrent('runs in parallel', () => {})
// Benchmarks
bench('sorting array', () => {
;[1, 5, 3, 2, 4].sort()
})
Hooks
import { beforeEach, afterEach, beforeAll, afterAll } from 'vitest'
beforeAll(() => {
// Run once before all tests
})
beforeEach(() => {
// Run before each test
})
afterEach(() => {
// Run after each test
})
afterAll(() => {
// Run once after all tests
})
For detailed test patterns (tags, filtering, fixtures), see references/test-patterns.md.
Assertions (Expect)
Common Assertions
import { expect } from 'vitest'
// Equality
expect(actual).toBe(expected) // Strict equality (===)
expect(actual).toEqual(expected) // Deep equality
expect(actual).toStrictEqual(expected) // Strict deep equality
// Truthiness
expect(value).toBeTruthy()
expect(value).toBeFalsy()
expect(value).toBeNull()
expect(value).toBeUndefined()
expect(value).toBeDefined()
// Numbers
expect(value).toBeGreaterThan(5)
expect(value).toBeLessThan(10)
expect(value).toBeCloseTo(0.3, 5) // Floating point
// Strings
expect(str).toContain('substring')
expect(str).toMatch(/regex/)
expect(str).toHaveLength(5)
// Arrays
expect(array).toContain(item)
expect(array).toHaveLength(3)
expect(array).toContainEqual({ id: 1 })
// Objects
expect(obj).toHaveProperty('nested.path')
expect(obj).toMatchObject({ name: 'test' })
// Errors
expect(() => fn()).toThrow()
expect(() => fn()).toThrow(Error)
expect(() => fn()).toThrow('error message')
// Async
await expect(promise).resolves.toBe(value)
await expect(promise).rejects.toThrow()
Negation
expect(value).not.toBe(true)
expect(array).not.toContain(item)
For detailed assertions and custom matchers, see references/assertions.md.
Mocking
Function Mocks
import { vi } from 'vitest'
// Create mock function
const mockFn = vi.fn()
mockFn('hello')
expect(mockFn).toHaveBeenCalledWith('hello')
// Mock implementation
const mock = vi.fn(() => 'default')
mock.mockReturnValue('value')
mock.mockImplementation(() => 'impl')
mock.mockResolvedValue('async value')
Module Mocks
import { vi } from 'vitest'
// Mock entire module
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ id: 1 })),
fetchPosts: vi.fn(() => Promise.resolve([]))
}))
// Partial mock (keep some originals)
vi.mock('./utils', async (importOriginal) => {
const mod = await importOriginal()
return {
...mod,
formatDate: vi.fn(() => 'mocked date')
}
})
Spy On
import { vi } from 'vitest'
import * as utils from './utils'
vi.spyOn(utils, 'formatDate').mockReturnValue('2024-01-01')
vi.spyOn(console, 'log').mockImplementation(() => {})
Timers
import { vi } from 'vitest'
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
it('advances time', () => {
const callback = vi.fn()
setTimeout(callback, 1000)
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalled()
})
For detailed mocking (globals, dates, file system), see references/mocking.md.
Configuration
Basic Config
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['src/**/*.{test,spec}.{ts,tsx}'],
exclude: ['node_modules', 'dist'],
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
},
},
})
Using Vite Config
// vite.config.ts
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/setup-tests.ts',
},
})
Environment Options
| Environment | Use Case |
|---|---|
node |
Backend/Node.js code |
jsdom |
DOM simulation |
happy-dom |
Faster DOM alternative |
edge-runtime |
Edge workers |
browser |
Real browser testing |
For detailed configuration, see references/configuration.md.
Component Testing (Browser Mode)
Setup
npx vitest init browser
# or manually
npm install -D @vitest/browser-playwright
Configuration
import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
browser: {
enabled: true,
provider: playwright(),
instances: [
{ browser: 'chromium' },
],
},
},
})
React Component Test
import { render } from 'vitest-browser-react'
import { page } from 'vitest/browser'
import { expect, test } from 'vitest'
import LoginForm from './LoginForm'
test('login form submits credentials', async () => {
const onSubmit = vi.fn()
const screen = render(<LoginForm onSubmit={onSubmit} />)
await screen.getByLabelText(/email/i).fill('user@example.com')
await screen.getByLabelText(/password/i).fill('password123')
await screen.getByRole('button', { name: /login/i }).click()
expect(onSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123'
})
})
Vue Component Test
import { render } from 'vitest-browser-vue'
import { expect, test } from 'vitest'
import Counter from './Counter.vue'
test('counter increments', async () => {
const screen = render(Counter, { count: 0 })
await expect.element(screen.getByText('0')).toBeInTheDocument()
await screen.getByRole('button', { name: /increment/i }).click()
await expect.element(screen.getByText('1')).toBeInTheDocument()
})
Browser Assertions
import { page, expect } from 'vitest/browser'
// DOM assertions
await expect.element(page.getByText('Hello')).toBeInTheDocument()
await expect.element(page.getByRole('button')).toBeVisible()
await expect.element(page.getByLabelText('Email')).toBeEnabled()
await expect.element(page.getByRole('heading')).toHaveTextContent('Title')
await expect.element(page.getByTestId('alert')).toHaveClass('error')
For detailed browser testing (locators, interactions, visual regression), see references/browser-testing.md.
Snapshots
Creating Snapshots
import { expect, test } from 'vitest'
test('matches snapshot', () => {
const ui = renderComponent()
expect(ui).toMatchSnapshot()
})
test('matches inline snapshot', () => {
expect({ name: 'test' }).toMatchInlineSnapshot(`
{
"name": "test"
}
`)
})
Updating Snapshots
vitest -u # Update all snapshots
vitest -u --run # Update without watch mode
For detailed snapshot patterns, see references/snapshots.md.
Debugging Failed Tests
When a test fails, follow this systematic workflow:
Step 1: Isolate the Failure
# Run only the failing test file
vitest run path/to/failing.test.ts
# Run specific test by name
vitest run -t "test name pattern"
# Disable parallelism for clearer errors
vitest run --no-threads
Step 2: Examine the Output
# Use verbose reporter for details
vitest run --reporter=verbose
# The diff output shows expected vs received
# Example output:
# AssertionError: expected 2 to deeply equal 3
# - Expected: 3
# + Received: 2
Step 3: Common Fixes
| Error Pattern | Likely Cause | Fix |
|---|---|---|
expected X to equal Y |
Wrong value or logic error | Check the assertion and code logic |
Cannot read property X of undefined |
Missing mock or initialization | Add setup or mock |
Timeout exceeded |
Async not completing | Add await or increase timeout |
vi.mock is not hoisted |
Import before mock | Move mock to top of file |
Snapshot is outdated |
Intentional change | Run vitest -u to update |
Step 4: Fix and Verify
# After fixing, run the test again
vitest run path/to/failing.test.ts
# Then run full suite to catch regressions
vitest run
Step 5: Debug with Breakpoints (if needed)
# Debug in VS Code: click "Debug Test" above the test
# Or use Node inspector:
node --inspect-brk ./node_modules/.bin/vitest run --no-threads
For detailed debugging techniques, see references/debugging.md.
Coverage
Configuration
export default defineConfig({
test: {
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.ts'],
exclude: ['src/**/*.test.ts'],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80
}
}
}
})
Running Coverage
vitest run --coverage
vitest run --coverage --reporter=json
Coverage Validation Checkpoint
After running coverage, verify thresholds passed before proceeding:
# Exit code 0 = all thresholds met
vitest run --coverage && echo "✅ Coverage thresholds passed"
# Check specific coverage in CI
if vitest run --coverage; then
echo "Coverage check passed - safe to merge"
else
echo "Coverage check failed - review report"
exit 1
fi
Coverage report locations:
coverage/index.html— Visual reportcoverage/lcov.info— For codecov/upload
For detailed coverage configuration, see references/coverage.md.
Test Projects (Monorepo)
Workspace Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
projects: [
'packages/*',
{
test: {
name: 'unit',
include: ['tests/unit/**/*.test.ts'],
environment: 'node',
},
},
{
test: {
name: 'browser',
include: ['tests/browser/**/*.test.ts'],
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
},
],
},
})
For detailed workspace patterns, see references/projects.md.
CI/CD Integration
GitHub Actions
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx vitest run --coverage
- uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
Browser Tests in CI
- run: npx playwright install --with-deps chromium
- run: npx vitest run --browser.headless
For detailed CI/CD patterns, see references/ci-cd.md.
Reference Files
For deep dives into specific topics, read these files:
Getting Started
references/cli.md— Complete CLI reference, all commands and optionsreferences/configuration.md— All config options, environments, setup filesreferences/ide-integration.md— VS Code, JetBrains, and other IDE setup
Writing Tests
references/test-patterns.md— Tags, filtering, fixtures, test contextreferences/test-annotations.md— Adding context to tests for debugging and reportingreferences/snapshots.md— Snapshot patterns, property matchers
Assertions & Mocking
references/assertions.md— All matchers, custom matchers, extending expectreferences/mocking.md— Functions, modules, globals, timers, file system, requests, classes, dates
Running Tests
references/parallelism.md— File and test parallelism configurationreferences/reporters.md— All built-in reporters and custom reporter creationreferences/coverage.md— Coverage providers, thresholds, reporters
Browser & E2E
references/browser-testing.md— Locators, interactions, visual regression, providersreferences/component-testing.md— React, Vue, Svelte testing patterns
Advanced
references/projects.md— Monorepo, workspaces, multiple configsreferences/testing-types.md— Type checking withexpectTypeOfandassertTypereferences/in-source-testing.md— Inline testing within source files
Troubleshooting
references/debugging.md— Debug tools, breakpoints, and techniquesreferences/flaky-tests.md— Flaky test detection, handling, quarantinereferences/common-errors.md— Solutions to frequent issuesreferences/ci-cd.md— GitHub Actions, Docker, caching, parallelization
Official Documentation
- Vitest Docs: https://vitest.dev/
- API Reference: https://vitest.dev/api/
- Configuration: https://vitest.dev/config/
- GitHub: https://github.com/vitest-dev/vitest
Some parts of this skill are adapted from Everything Claude Code (credit: Affaan Mustafa) under MIT license.
More from mguinada/agent-skills
refactor
TDD-based code refactoring preserving behavior through tests. Use Red-Green-Refactor cycles to apply refactoring patterns one test-verified change at a time. **TRIGGERS**: 'clean up code', 'make code simpler', 'reduce complexity', 'refactor this', 'apply DRY', 'extract method', 'remove duplication'. **DISTINCT FROM**: Adding features (use /tdd) or fixing bugs. **PROACTIVE**: Auto-invoke when test-covered code has complexity (functions >50 lines, high cyclomatic complexity, duplication).
16ai-engineering
Build AI agents and agentic workflows. Use when designing/building/debugging agentic systems: choosing workflows vs agents, implementing prompt patterns (chaining/routing/parallelization/orchestrator-workers/evaluator-optimizer), building autonomous agents with tools, designing ACI/tool specs, or troubleshooting/optimizing implementations. **PROACTIVE ACTIVATION**: Auto-invoke when building agentic applications, designing workflows vs agents, or implementing agent patterns. **DETECTION**: Check for agent code (MCP servers, tool defs, .mcp.json configs), or user mentions of \"agent\", \"workflow\", \"agentic\", \"autonomous\". **USE CASES**: Designing agentic systems, choosing workflows vs agents, implementing prompt patterns, building agents with tools, designing ACI/tool specs, troubleshooting/optimizing agents.
13git-commit
Generate concise, descriptive git commit messages following best practices. Use when creating git commits from staged changes, crafting commit messages, or reviewing commit message quality. Use when the user says /commit or asks to create a git commit. **PROACTIVE ACTIVATION**: Auto-invoke when staged changes detected or user asks to commit/save work. **DETECTION**: Run git status - if staged changes exist, offer to commit. User says \"commit\", \"save\", \"done with feature\". **USE CASES**: Staged changes detected, work completed, user wants to save progress.
12tdd
Guide Test-Driven Development workflow (Red-Green-Refactor) for new features, bug fixes, and refactoring. Supports both Python (pytest) and Ruby (RSpec). Use when writing tests, implementing features, or following TDD methodology. **PROACTIVE ACTIVATION**: Auto-invoke when implementing features or fixing bugs in projects with test infrastructure. **DETECTION**: Check for tests/ directory, pytest.ini, pyproject.toml with pytest config, spec/ directory, .rspec file, or *_spec.rb files. **USE CASES**: Writing production code, fixing bugs, adding features, legacy code characterization.
11prompt-engineering
Creates system prompts, writes tool descriptions, and structures agent instructions for agentic systems. Use when the user asks to create, generate, or design prompts for AI agents, especially for tool-using agents, planning agents, or autonomous systems. **PROACTIVE ACTIVATION**: Auto-invoke when designing prompts for agents, tools, or agentic workflows in AI projects. **DETECTION**: Check for agent/tool-related code, prompt files, or user mentions of \"prompt\", \"agent\", \"LLM\". **USE CASES**: Designing system prompts, tool descriptions, agent instructions, prompt optimization, reducing hallucinations.
10copilot-sdk
Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent. **PROACTIVE ACTIVATION**: Auto-invoke when building agentic applications or integrating Copilot SDK. **DETECTION**: Check for @github/copilot-sdk imports, copilot dependencies in package.json/pyproject.toml/go.mod. **USE CASES**: Embedding agents in apps, creating custom tools, implementing streaming, managing sessions, connecting to MCP servers.
9