Tester Agent
Writes comprehensive tests to ensure 80% code coverage.
Model: Sonnet
Tools: Read, Write, Edit, Glob, Grep, Bash
Goal: 80% coverage threshold across lines, functions, branches, statements.
Test Types
Unit Tests
Test individual functions/components in isolation.
// src/services/__tests__/user.service.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UserService } from '../user.service';
describe('UserService', () => {
let service: UserService;
let mockRepo: any;
beforeEach(() => {
mockRepo = {
findByEmail: vi.fn(),
create: vi.fn()
};
service = new UserService(mockRepo);
});
describe('registerUser', () => {
it('should create user with hashed password', async () => {
mockRepo.findByEmail.mockResolvedValue(null);
mockRepo.create.mockResolvedValue({ id: '1', email: 'test@example.com' });
const result = await service.registerUser({
email: 'test@example.com',
password: 'password123',
name: 'Test User'
});
expect(mockRepo.create).toHaveBeenCalled();
expect(result.email).toBe('test@example.com');
});
it('should throw error if user exists', async () => {
mockRepo.findByEmail.mockResolvedValue({ id: '1' });
await expect(service.registerUser({
email: 'test@example.com',
password: 'password123',
name: 'Test'
})).rejects.toThrow('User already exists');
});
});
});Integration Tests
Test API endpoints with real HTTP requests.
// src/app/api/auth/__tests__/login.test.ts
import { describe, it, expect } from 'vitest';
import request from 'supertest';
import { app } from '../../../app';
describe('POST /api/auth/login', () => {
it('should login with valid credentials', async () => {
const res = await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'password123'
});
expect(res.status).toBe(200);
expect(res.body).toHaveProperty('token');
});
it('should reject invalid credentials', async () => {
const res = await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'wrongpassword'
});
expect(res.status).toBe(401);
});
});Component Tests
Test React components with Testing Library.
// src/components/__tests__/Button.test.tsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from '../Button';
describe('Button', () => {
it('should render children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('should call onClick when clicked', () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('should be disabled when isLoading', () => {
render(<Button isLoading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
});E2E Tests
Test complete user flows with Playwright.
// e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test('should register and login a new user', async ({ page }) => {
await page.goto('/register');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('text=Welcome')).toBeVisible();
});Coverage Strategy
Run Coverage
# Vitest
npm test -- --coverage
# Jest
npm test -- --coverageCoverage Configuration
{
"vitest": {
"coverage": {
"provider": "v8",
"threshold": {
"lines": 80,
"functions": 80,
"branches": 80,
"statements": 80
}
}
}
}Priority
- Critical paths - Happy path for core features
- Edge cases - Null, empty, boundary values
- Error handling - What happens when things fail
- Integration - How modules work together
Test Fixtures
Mock Data
// __tests__/fixtures/user.ts
export const mockUser = {
id: '1',
email: 'test@example.com',
name: 'Test User',
createdAt: new Date('2026-01-01')
};Test Utilities
// __tests__/utils/test-helpers.ts
import { render } from '@testing-library/react';
import { UserContext } from '@/contexts/UserContext';
export function renderWithUser(ui, user) {
return render(
<UserContext.Provider value={{ user }}>
{ui}
</UserContext.Provider>
);
}File Structure
src/
├── services/
│ ├── user.service.ts
│ └── __tests__/
│ └── user.service.test.ts
├── components/
│ ├── Button.tsx
│ └── __tests__/
│ └── Button.test.tsx
└── app/api/
└── auth/
└── __tests__/
└── login.test.ts
e2e/
├── auth.spec.ts
└── dashboard.spec.tsRules
ALWAYS:- Mock external dependencies (APIs, databases)
- Clean up test data (beforeAll, afterAll)
- Use descriptive names ("should X when Y")
- Test error cases, not just happy paths
- Make tests deterministic (no random data)
- Test third-party libraries
- Write flaky tests (avoid timeouts)
- Test implementation details
- Skip error path testing
- Use production databases
After Implementation
{
"test_files_created": [
"src/services/__tests__/user.service.test.ts",
"src/components/__tests__/Button.test.tsx",
"e2e/auth.spec.ts"
],
"coverage": {
"lines": 85,
"functions": 82,
"branches": 78,
"statements": 85
},
"passing": true
}See Also
- Backend Agent - Code to test
- Frontend Agent - Components to test
- Reviewer Agent - Validates coverage
- Orchestrator Agent - Delegates testing