| name | testing-patterns-vitest |
| description | Vitest testing conventions and patterns for @j0kz/mcp-agents including test organization, naming, assertion patterns, mocking strategies, and coverage targets (75%). Use when writing tests, organiz... |
Vitest Testing Patterns for @j0kz/mcp-agents
Comprehensive testing patterns and conventions using Vitest 3.2.4 across the monorepo.
When to Use This Skill
- Writing new test files
- Organizing test suites
- Debugging test failures
- Improving test coverage
- Setting up mocks and spies
- Writing integration tests
Evidence Base
Current Testing Stats:
- 632+ passing tests across 11 packages
- Vitest 3.2.4 with v8 coverage
- 75% coverage target (packages/shared/vitest.config.ts)
- Test files: *.test.ts pattern
- Parallel execution (max 4 threads)
Quick Start
Generate Tests Automatically
# Generate comprehensive tests for any file
npx @j0kz/test-generator@latest generate src/your-file.ts
# With specific framework
npx @j0kz/test-generator@latest generate src/your-file.ts --framework=vitest
# Include edge cases
npx @j0kz/test-generator@latest generate src/your-file.ts --edge-cases --error-cases
Run Tests
# Run all tests
npm test
# Run specific package tests
npm test -- packages/smart-reviewer-mcp
# Watch mode
npm test -- --watch
# Coverage report
npm test -- --coverage
# Run specific test file
npx vitest run src/analyzer.test.ts
Test Organization
File Structure
packages/
your-package/
src/
analyzer.ts # Source file
tests/
analyzer.test.ts # Test file
vitest.config.ts # Package config
Test File Template
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { YourClass } from '../src/your-class.js';
describe('YourClass', () => {
let instance: YourClass;
beforeEach(() => {
instance = new YourClass();
});
afterEach(() => {
vi.clearAllMocks();
});
describe('methodName', () => {
it('should handle normal case', () => {
// Arrange
const input = 'test';
// Act
const result = instance.methodName(input);
// Assert
expect(result).toBe('expected');
});
it('should handle edge case', () => {
// Test edge cases
});
it('should handle error case', () => {
// Test error handling
});
});
});
Assertion Patterns
For comprehensive assertion patterns and examples:
cat .claude/skills/testing-patterns-vitest/references/assertion-patterns-guide.md
Quick Reference
// Basic assertions
expect(value).toBe(expected);
expect(value).toEqual(expected);
expect(value).toBeDefined();
expect(value).toBeTruthy();
// Arrays
expect(array).toContain(item);
expect(array).toHaveLength(3);
// Objects
expect(obj).toHaveProperty('key');
expect(obj).toMatchObject({ key: 'value' });
// Errors
expect(() => fn()).toThrow();
expect(() => fn()).toThrow('specific message');
// Async
await expect(promise).resolves.toBe(value);
await expect(promise).rejects.toThrow();
Mocking Strategies
For detailed mocking patterns:
cat .claude/skills/testing-patterns-vitest/references/mocking-strategies-guide.md
Quick Examples
// Mock module
vi.mock('../src/utils.js', () => ({
utilFunction: vi.fn().mockReturnValue('mocked')
}));
// Spy on method
const spy = vi.spyOn(instance, 'method');
expect(spy).toHaveBeenCalledWith('arg');
// Mock file system
vi.mock('fs/promises', () => ({
readFile: vi.fn().mockResolvedValue('content'),
writeFile: vi.fn().mockResolvedValue(undefined)
}));
Coverage Guidelines
For comprehensive coverage guide:
cat .claude/skills/testing-patterns-vitest/references/coverage-guide.md
Coverage Targets
- Minimum: 75% overall
- Goal: 80%+ for critical packages
- Exclude: dist/, node_modules/, *.config.ts
Check Coverage
# Generate coverage report
npm test -- --coverage
# Open HTML report
open coverage/index.html
# Check if meeting threshold
npm run test:coverage:check
Common Test Patterns
Testing Async Functions
it('should handle async operations', async () => {
const result = await asyncFunction();
expect(result).toBe('expected');
});
it('should handle async errors', async () => {
await expect(asyncFunction()).rejects.toThrow('error message');
});
Testing Classes
describe('MyClass', () => {
let instance: MyClass;
beforeEach(() => {
instance = new MyClass();
});
it('should initialize with defaults', () => {
expect(instance.property).toBe('default');
});
it('should update state', () => {
instance.updateState('new');
expect(instance.property).toBe('new');
});
});
Testing with Files
import { vi } from 'vitest';
import * as fs from 'fs/promises';
vi.mock('fs/promises');
it('should read file', async () => {
vi.mocked(fs.readFile).mockResolvedValue('content');
const result = await readConfig('config.json');
expect(fs.readFile).toHaveBeenCalledWith('config.json', 'utf-8');
expect(result).toBe('content');
});
Debugging Test Failures
For comprehensive debugging strategies:
cat .claude/skills/testing-patterns-vitest/references/debugging-test-failures-guide.md
Quick Debug Tips
# Run single test with verbose output
npx vitest run src/analyzer.test.ts --reporter=verbose
# Run with Node debugging
node --inspect-brk ./node_modules/.bin/vitest run
# Show full error stacks
npx vitest run --full-trace
# Run specific test by name
npx vitest run -t "should handle edge case"
Integration Testing
Testing MCP Tools
describe('MCP Tool Integration', () => {
it('should handle tool execution', async () => {
const server = new Server();
const request = {
method: 'tools/call',
params: {
name: 'analyze',
arguments: { path: './src' }
}
};
const response = await server.handleRequest(request);
expect(response).toHaveProperty('success', true);
expect(response).toHaveProperty('data');
});
});
Testing with Real Files
import { mkdtemp, rm } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
describe('File Operations', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), 'test-'));
});
afterEach(async () => {
await rm(tempDir, { recursive: true });
});
it('should process files', async () => {
// Use tempDir for test files
});
});
Best Practices
- Follow AAA Pattern: Arrange, Act, Assert
- One assertion per test (when practical)
- Descriptive test names that explain what and why
- Test behavior, not implementation
- Clean up after tests (restore mocks, delete temp files)
- Group related tests with describe blocks
- Test edge cases and errors, not just happy path
- Keep tests fast (<100ms per test ideal)
- Avoid test interdependence
- Use data-driven tests for multiple scenarios
Example: Complete Test Suite
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { SecurityScanner } from '../src/scanner.js';
import * as fs from 'fs/promises';
vi.mock('fs/promises');
describe('SecurityScanner', () => {
let scanner: SecurityScanner;
beforeEach(() => {
scanner = new SecurityScanner();
vi.clearAllMocks();
});
describe('scan', () => {
it('should detect SQL injection', async () => {
const code = "query = 'SELECT * FROM users WHERE id = ' + userId;";
const result = await scanner.scan(code);
expect(result.issues).toHaveLength(1);
expect(result.issues[0].type).toBe('sql-injection');
});
it('should handle file read errors', async () => {
vi.mocked(fs.readFile).mockRejectedValue(new Error('File not found'));
await expect(scanner.scanFile('missing.ts')).rejects.toThrow('File not found');
});
it('should skip binary files', async () => {
const result = await scanner.scan('image.png');
expect(result.skipped).toBe(true);
expect(result.reason).toBe('binary file');
});
});
});
Verification: Run npm test to see 632+ tests passing!