| name | generate-tests |
| description | Generate comprehensive test files for new or existing code. Creates test suites with proper mocking, edge cases, and error handling. |
Generate Tests Skill
Generate Vitest test files for new or existing code. Creates comprehensive test suites following project conventions and best practices.
When to Use
- After implementing new features
- After creating new API routes
- After creating new utility functions
- When refactoring existing code
- When requested with
/generate-tests
Instructions
Phase 1: Analyze Target Code
Goal: Understand what needs to be tested
-
Identify the target:
git diff --name-only HEAD~5
-
Categorize the code type:
- API Routes (
app/api/**/*.ts) → Integration tests with MSW
- Utility Functions (
lib/**/*.ts, src/utils/**/*.ts) → Unit tests
- React Components (
src/components/**/*.tsx) → Component tests with React Testing Library
- Hooks (
hooks/**/*.ts) → Hook tests with renderHook
- Context Providers (
src/context/**/*.tsx) → Context tests
-
Identify test requirements:
- Input/output contracts
- Edge cases
- Error handling
- Dependencies to mock
Phase 2: Generate Test File
Goal: Create comprehensive test suite following project patterns
Test File Location
src/component.tsx → tests/components/component.test.tsx
app/api/route.ts → tests/api/route-name.test.ts
lib/utility.ts → tests/lib/utility.test.ts
hooks/useHook.ts → tests/hooks/useHook.test.ts
Test File Template
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { functionUnderTest } from '@/path/to/code';
import { createMockUser, createMockProject } from '@/tests/setup';
describe('[Component/Function Name]', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.resetAllMocks();
});
describe('[function/feature name]', () => {
it('should [expected behavior] when [condition]', () => {
const input = ;
const result = functionUnderTest(input);
expect(result).toBe();
});
it('should handle [edge case]', () => {
});
it('should throw when [error condition]', () => {
expect(() => functionUnderTest(invalidInput)).toThrow();
});
});
});
Phase 3: Test Patterns by Code Type
API Route Tests
import { describe, it, expect } from 'vitest';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
const API_BASE = 'http://localhost:3001';
const handlers = [
http.get(`${API_BASE}/api/resource`, () => {
return HttpResponse.json({ items: [] });
}),
http.post(`${API_BASE}/api/resource`, async ({ request }) => {
const body = await request.json();
return HttpResponse.json({ item: { id: '1', ...body } }, { status: 201 });
}),
];
const server = setupServer(...handlers);
describe('Resource API', () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('GET /api/resource', () => {
it('should return list of resources', async () => {
const response = await fetch(`${API_BASE}/api/resource`);
const data = await response.json();
expect(response.ok).toBe(true);
expect(data.items).toEqual([]);
});
});
describe('POST /api/resource', () => {
it('should create a new resource', async () => {
const response = await fetch(`${API_BASE}/api/resource`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Test' }),
});
expect(response.status).toBe(201);
const data = await response.json();
expect(data.item.name).toBe('Test');
});
});
describe('Error handling', () => {
it('should handle server errors', async () => {
server.use(
http.get(`${API_BASE}/api/resource`, () => {
return HttpResponse.json({ error: 'Server error' }, { status: 500 });
})
);
const response = await fetch(`${API_BASE}/api/resource`);
expect(response.status).toBe(500);
});
});
});
Utility Function Tests
import { describe, it, expect, vi } from 'vitest';
import { utilityFunction } from '@/lib/utility';
describe('utilityFunction', () => {
it('should return expected output for valid input', () => {
expect(utilityFunction('input')).toBe('expected');
});
it('should handle empty input', () => {
expect(utilityFunction('')).toBe('');
});
it('should handle null/undefined', () => {
expect(utilityFunction(null)).toBeNull();
expect(utilityFunction(undefined)).toBeUndefined();
});
it('should throw for invalid input', () => {
expect(() => utilityFunction(-1)).toThrow('Invalid input');
});
});
React Component Tests
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { Component } from '@/src/components/Component';
vi.mock('@/hooks/useApi', () => ({
api: {
get: vi.fn(),
post: vi.fn(),
},
}));
describe('Component', () => {
it('should render correctly', () => {
render(<Component />);
expect(screen.getByRole('heading')).toBeInTheDocument();
});
it('should handle user interaction', async () => {
const onSubmit = vi.fn();
render(<Component onSubmit={onSubmit} />);
fireEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalled();
});
});
it('should display loading state', () => {
render(<Component isLoading />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
it('should display error state', () => {
render(<Component error="Something went wrong" />);
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
});
});
Hook Tests
import { describe, it, expect, vi } from 'vitest';
import { renderHook, act, waitFor } from '@testing-library/react';
import { useCustomHook } from '@/hooks/useCustomHook';
describe('useCustomHook', () => {
it('should return initial state', () => {
const { result } = renderHook(() => useCustomHook());
expect(result.current.data).toBeNull();
expect(result.current.isLoading).toBe(false);
});
it('should update state on action', async () => {
const { result } = renderHook(() => useCustomHook());
act(() => {
result.current.doSomething();
});
await waitFor(() => {
expect(result.current.data).toBeDefined();
});
});
});
Phase 4: Test Coverage Checklist
For each piece of code, ensure tests cover:
Functions
API Routes
Components
Hooks
Phase 5: Add Mock Handlers (if needed)
If tests need new API mocks, add to tests/mocks/handlers.ts:
http.get(`${API_BASE}/api/new-endpoint`, () => {
return HttpResponse.json({ });
}),
http.post(`${API_BASE}/api/new-endpoint`, async ({ request }) => {
const body = await request.json();
return HttpResponse.json({ }, { status: 201 });
}),
Phase 6: Run and Verify Tests
npm test
npm test tests/api/my-feature.test.ts
npm run test:watch
npm run test:coverage
Expected output:
- All tests pass ✓
- Coverage meets minimum thresholds
- No console errors or warnings
Quick Reference
| Code Type | Test Location | Key Imports |
|---|
| API Route | tests/api/ | http, HttpResponse, setupServer from msw |
| Utility | tests/lib/ | vi from vitest |
| Component | tests/components/ | render, screen, fireEvent from RTL |
| Hook | tests/hooks/ | renderHook, act from RTL |
| Context | tests/context/ | Wrapper component pattern |
Mock Patterns
vi.mock('@/lib/module', () => ({
functionName: vi.fn(),
}));
vi.mocked(functionName).mockReturnValue('value');
vi.mocked(functionName).mockResolvedValue('async value');
vi.mocked(functionName).mockRejectedValue(new Error('error'));
process.env.VAR_NAME = 'value';
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ data: 'value' }),
});
Example Flow
User: /generate-tests for the database backup feature
Claude: I'll generate tests for the database backup functionality.
[Phase 1: Analyze target code]
Found code at lib/database-backup.ts with:
- 3 exported functions: createBackup, listBackups, cleanupAutoBackups
- 1 interface: BackupMetadata
- Dependencies: fs, pg, neon
[Phase 2: Generate test structure]
Creating: tests/lib/database-backup.test.ts
[Phase 3: Apply patterns]
- Unit tests for each function
- Mocking fs, pg, and neon modules
- Testing success and error cases
[Phase 4: Coverage checklist]
✓ createBackup: local source, neon source, auto-backup flag, missing config
✓ listBackups: empty, sorted results, missing metadata
✓ cleanupAutoBackups: below threshold, above threshold, manual backups ignored
[Phase 5: No new mock handlers needed]
[Phase 6: Run tests]
$ npm test tests/lib/database-backup.test.ts
✓ database-backup (12 tests)
All tests pass!
Notes
- Always follow AAA pattern (Arrange, Act, Assert)
- Keep tests focused - one assertion per test when possible
- Use descriptive test names that explain the expected behavior
- Mock external dependencies (database, file system, network)
- Test both success and failure paths
- Don't test implementation details, test behavior
- Use factory functions from
tests/setup.ts for mock data