Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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 -- --coverage

Coverage Configuration

{
  "vitest": {
    "coverage": {
      "provider": "v8",
      "threshold": {
        "lines": 80,
        "functions": 80,
        "branches": 80,
        "statements": 80
      }
    }
  }
}

Priority

  1. Critical paths - Happy path for core features
  2. Edge cases - Null, empty, boundary values
  3. Error handling - What happens when things fail
  4. 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.ts

Rules

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)
NEVER:
  • 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