Testing Expert

The Testing Expert agent specializes in software testing strategies, test implementation, and quality assurance.

Expertise Areas#

  • Unit Testing - Jest, Vitest, Mocha
  • Integration Testing - API testing, database testing
  • E2E Testing - Playwright, Cypress, Puppeteer
  • Component Testing - React Testing Library, Vue Test Utils
  • Test Strategy - TDD, BDD, coverage goals
  • Mocking - Spies, stubs, mocks, fixtures
  • CI Integration - Test automation, reporting

Usage Examples#

Test Strategy#

Use the testing-expert agent to create a test strategy for a new e-commerce feature.

Response includes:

  • Test pyramid breakdown
  • Coverage goals
  • Critical paths
  • Test data strategy

Unit Tests#

Use the testing-expert agent to write unit tests for this utility function.

Response includes:

  • Test cases
  • Edge cases
  • Mock setup
  • Assertions

E2E Tests#

Use the testing-expert agent to write Playwright tests for the checkout flow.

Response includes:

  • Page objects
  • Test scenarios
  • Assertions
  • Error handling

Testing Patterns#

Test Structure (AAA Pattern)#

1describe('UserService', () => { 2 describe('createUser', () => { 3 it('should create a user with valid data', async () => { 4 // Arrange 5 const userData = { email: 'test@example.com', name: 'Test' }; 6 const mockRepo = { create: vi.fn().mockResolvedValue({ id: '1', ...userData }) }; 7 const service = new UserService(mockRepo); 8 9 // Act 10 const result = await service.createUser(userData); 11 12 // Assert 13 expect(result).toEqual({ id: '1', ...userData }); 14 expect(mockRepo.create).toHaveBeenCalledWith(userData); 15 }); 16 17 it('should throw on invalid email', async () => { 18 // Arrange 19 const userData = { email: 'invalid', name: 'Test' }; 20 const service = new UserService(mockRepo); 21 22 // Act & Assert 23 await expect(service.createUser(userData)) 24 .rejects.toThrow('Invalid email'); 25 }); 26 }); 27});

Component Testing#

1import { render, screen, fireEvent } from '@testing-library/react'; 2import { LoginForm } from './LoginForm'; 3 4describe('LoginForm', () => { 5 it('should submit with valid credentials', async () => { 6 const onSubmit = vi.fn(); 7 render(<LoginForm onSubmit={onSubmit} />); 8 9 // Fill form 10 await userEvent.type(screen.getByLabelText('Email'), 'test@example.com'); 11 await userEvent.type(screen.getByLabelText('Password'), 'password123'); 12 13 // Submit 14 await userEvent.click(screen.getByRole('button', { name: 'Sign In' })); 15 16 // Assert 17 expect(onSubmit).toHaveBeenCalledWith({ 18 email: 'test@example.com', 19 password: 'password123', 20 }); 21 }); 22 23 it('should show validation errors', async () => { 24 render(<LoginForm onSubmit={vi.fn()} />); 25 26 // Submit empty form 27 await userEvent.click(screen.getByRole('button', { name: 'Sign In' })); 28 29 // Assert errors shown 30 expect(screen.getByText('Email is required')).toBeInTheDocument(); 31 expect(screen.getByText('Password is required')).toBeInTheDocument(); 32 }); 33});

E2E Testing with Playwright#

1import { test, expect } from '@playwright/test'; 2 3test.describe('Checkout Flow', () => { 4 test.beforeEach(async ({ page }) => { 5 // Login and add item to cart 6 await page.goto('/login'); 7 await page.fill('[name="email"]', 'test@example.com'); 8 await page.fill('[name="password"]', 'password'); 9 await page.click('button[type="submit"]'); 10 await page.waitForURL('/dashboard'); 11 }); 12 13 test('should complete checkout', async ({ page }) => { 14 // Navigate to cart 15 await page.click('[data-testid="cart-icon"]'); 16 await expect(page.getByText('Your Cart')).toBeVisible(); 17 18 // Proceed to checkout 19 await page.click('text=Checkout'); 20 await expect(page).toHaveURL('/checkout'); 21 22 // Fill shipping info 23 await page.fill('[name="address"]', '123 Main St'); 24 await page.fill('[name="city"]', 'New York'); 25 await page.selectOption('[name="state"]', 'NY'); 26 await page.fill('[name="zip"]', '10001'); 27 28 // Complete order 29 await page.click('text=Place Order'); 30 await expect(page.getByText('Order Confirmed')).toBeVisible(); 31 }); 32});

API Testing#

1import { describe, it, expect, beforeAll, afterAll } from 'vitest'; 2import request from 'supertest'; 3import { app } from '../app'; 4import { prisma } from '../lib/prisma'; 5 6describe('POST /api/users', () => { 7 beforeAll(async () => { 8 await prisma.$connect(); 9 }); 10 11 afterAll(async () => { 12 await prisma.user.deleteMany(); 13 await prisma.$disconnect(); 14 }); 15 16 it('should create a user', async () => { 17 const response = await request(app) 18 .post('/api/users') 19 .send({ email: 'test@example.com', name: 'Test User' }) 20 .expect(201); 21 22 expect(response.body).toMatchObject({ 23 data: { 24 email: 'test@example.com', 25 name: 'Test User', 26 }, 27 }); 28 }); 29 30 it('should return 400 for invalid data', async () => { 31 const response = await request(app) 32 .post('/api/users') 33 .send({ email: 'invalid' }) 34 .expect(400); 35 36 expect(response.body.error.code).toBe('VALIDATION_ERROR'); 37 }); 38});

Mocking Strategies#

Function Mocks#

1// Simple mock 2const mockFn = vi.fn(); 3mockFn.mockReturnValue('result'); 4mockFn.mockResolvedValue('async result'); 5 6// Spy on existing function 7const spy = vi.spyOn(object, 'method'); 8spy.mockImplementation(() => 'mocked');

Module Mocks#

1// Mock entire module 2vi.mock('./api', () => ({ 3 fetchUser: vi.fn().mockResolvedValue({ id: '1', name: 'Test' }), 4})); 5 6// Partial mock 7vi.mock('./utils', async () => { 8 const actual = await vi.importActual('./utils'); 9 return { 10 ...actual, 11 formatDate: vi.fn().mockReturnValue('2024-01-01'), 12 }; 13});

API Mocks with MSW#

1import { rest } from 'msw'; 2import { setupServer } from 'msw/node'; 3 4const handlers = [ 5 rest.get('/api/users/:id', (req, res, ctx) => { 6 return res(ctx.json({ id: req.params.id, name: 'Test User' })); 7 }), 8 rest.post('/api/users', (req, res, ctx) => { 9 return res(ctx.status(201), ctx.json({ id: '1', ...req.body })); 10 }), 11]; 12 13const server = setupServer(...handlers); 14 15beforeAll(() => server.listen()); 16afterEach(() => server.resetHandlers()); 17afterAll(() => server.close());

Test Coverage Strategy#

Test TypeCoverage TargetFocus Areas
Unit80%+Business logic, utilities
Integration70%+API endpoints, database
E2ECritical pathsUser journeys, checkout
Component75%+Interactive elements

Sample Prompts#

TaskPrompt
Unit tests"Write unit tests for this reducer function"
Integration"Create integration tests for the payment API"
E2E tests"Write Playwright tests for user registration"
Mocking"Set up mocks for this external API dependency"
Coverage"Identify untested code paths in this module"

Configuration#

1// bootspring.config.js 2module.exports = { 3 agents: { 4 customInstructions: { 5 'testing-expert': ` 6 - Use Vitest for unit/integration tests 7 - Use Playwright for E2E tests 8 - Follow AAA pattern 9 - Include edge cases 10 - Mock external dependencies 11 `, 12 }, 13 }, 14 quality: { 15 thresholds: { 16 coverage: 80, 17 }, 18 }, 19};