Vitest Unit Testing
Write fast unit tests with Vitest, coverage reporting, mocking, and snapshots. Apply when testing utilities, components, services, or building test coverage.
git clone --depth 1 https://github.com/ThamJiaHe/claude-code-handbook /tmp/vitest-unit-testing && cp -r /tmp/vitest-unit-testing/skills/examples/vitest-unit-testing- ~/.claude/skills/vitest-unit-testingvitest-unit-testing-skill.md
# Vitest Unit Testing
Systematic unit testing with Vitest for fast, reliable test feedback.
## Overview
This Skill enforces:
- Test-driven development (TDD) with Vitest
- Component testing with React
- Mocking and spying
- Snapshot testing
- Code coverage reporting
- Watch mode for development
- Parallel test execution
Apply when writing unit tests, testing components, or building test coverage.
## Setup Vitest
### Install Dependencies
```bash
npm install -D vitest @vitest/ui
npm install -D @testing-library/react @testing-library/jest-dom
npm install -D jsdom # For DOM testing
```
### Configure vitest.config.ts
```ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'**/*.test.ts',
'**/*.spec.ts'
]
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
});
```
### Setup File
```ts
// vitest.setup.ts
import { expect, afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom';
// Cleanup after each test
afterEach(() => {
cleanup();
});
```
### Package.json Scripts
```json
{
"scripts": {
"test": "vitest",
"test:watch": "vitest --watch",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}
```
## Writing Unit Tests
### Basic Test Structure
```ts
// src/utils/helpers.test.ts
import { describe, it, expect } from 'vitest';
import { add, multiply } from './helpers';
describe('Math Helpers', () => {
describe('add', () => {
it('should add two numbers correctly', () => {
expect(add(2, 3)).toBe(5);
});
it('should handle negative numbers', () => {
expect(add(-5, 3)).toBe(-2);
});
it('should handle zero', () => {
expect(add(0, 5)).toBe(5);
});
});
describe('multiply', () => {
it('should multiply two numbers correctly', () => {
expect(multiply(3, 4)).toBe(12);
});
it('should return 0 when multiplying by 0', () => {
expect(multiply(5, 0)).toBe(0);
});
});
});
```
### Component Testing
```tsx
// src/components/Button.test.tsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button Component', () => {
it('renders button with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
it('calls onClick handler when clicked', async () => {
const handleClick = vi.fn();
const user = userEvent.setup();
render(<Button onClick={handleClick}>Click</Button>);
const button = screen.getByRole('button');
await user.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('renders disabled button', () => {
render(<Button disabled>Disabled</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});
```
## Mocking
### Mocking Functions
```ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('User Service', () => {
let mockFetch: any;
beforeEach(() => {
mockFetch = vi.fn();
});
it('fetches user data', async () => {
mockFetch.mockResolvedValue({
json: async () => ({ id: 1, name: 'Alice' })
});
// Use mockFetch in your function
const response = await mockFetch();
const data = await response.json();
expect(data).toEqual({ id: 1, name: 'Alice' });
expect(mockFetch).toHaveBeenCalledTimes(1);
});
it('handles fetch error', async () => {
mockFetch.mockRejectedValue(new Error('Network error'));
await expect(mockFetch()).rejects.toThrow('Network error');
});
});
```
### Mocking Modules
```ts
// src/services/api.test.ts
import { describe, it, expect, vi } from 'vitest';
import { fetchUsers } from './api';
// Mock the entire module
vi.mock('../lib/http', () => ({
get: vi.fn(() => Promise.resolve([{ id: 1, name: 'Alice' }]))
}));
describe('API Service', () => {
it('fetches users', async () => {
const users = await fetchUsers();
expect(users).toHaveLength(1);
expect(users[0].name).toBe('Alice');
});
});
```
### Partial Mocking
```ts
import { describe, it, expect, vi } from 'vitest';
vi.mock('../utils', async () => {
const actual = await vi.importActual('../utils');
return {
...actual,
dateUtil: {
now: () => new Date('2025-01-01')
}
};
});
```
## Spying
```ts
import { describe, it, expect, vi, spyOn } from 'vitest';
import { logger } from './logger';
describe('Spy on function', () => {
it('spies on console.log', () => {
const consoleSpy = spyOn(console, 'log');
console.log('test message');
expect(consoleSpy).toHaveBeenCalledWith('test message');
expect(consoleSpy).toHaveBeenCalledTimes(1);
consoleSpy.mockRestore();
});
});
```
## Async Testing
```ts
import { describe, it, expect, vi } from 'vitest';
describe('Async Operations', () => {
it('waits for promise to resolve', async () => {
const fetchData = () =>
new Promise(resolve =>
setTimeout(() => resolve('data'), 100)
);
const data = await fetchData();
expect(data).toBe('data');
});
it('handles async errors', async () => {
const failingFetch = () =>
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Network error')), 100)
);
await expect(failingFetch()).rejects.toThrow('Network error');
});
});
```
## Snapshot Testing
```tsx
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { Card } from './Card';Build REST APIs with proper error handling, status codes, request validation, response formatting, and rate limiting. Apply when creating API routes, handling errors, validating input, or designing API responses.
Harden REST and GraphQL APIs against common attack vectors. Apply when building API endpoints, implementing authentication, handling file uploads, or exposing APIs to external consumers.
Deploy Node.js applications on AWS using EC2, RDS, and managed services with security best practices. Apply when setting up AWS infrastructure, configuring databases, managing security, or optimizing costs.
Rapidly fix build failures, type errors, and lint issues with minimal diffs. Apply when builds fail, TypeScript reports errors, or CI/CD pipelines break. Focuses on getting the build green fast.
STRIDE-based threat modeling for application architecture. Apply when designing new systems, reviewing architecture, or assessing security posture of existing applications.
Production-ready Docker patterns for multi-stage builds, security hardening, and orchestration. Apply when creating Dockerfiles, docker-compose configs, or deploying containerized applications.
Enforces Conventional Commits, PR standards, merge conflict resolution, and branch management. Apply when committing code, opening PRs, resolving conflicts, managing branches, or handling Git operations.
Deploy Node.js applications on Google Cloud with Cloud Run, Cloud Firestore, and Google APIs. Implement OAuth2 authentication and manage service accounts. Apply when building serverless applications, integrating Google services, or deploying to GCP.