Testing Guide
π This is the Testing Guide - Learn fundamental testing concepts, frameworks, and basic TDD practices. For advanced testing patterns and enterprise-scale strategies, see Testing Patterns.
This comprehensive guide covers testing strategies, test-driven development (TDD), and test automation patterns for Claude Code projects.
Table of Contents
- Testing Philosophy
- Test-Driven Development (TDD)
- Testing Strategies
- Framework-Specific Guides
- Testing with Claude Code
- Best Practices
- Common Testing Patterns
- Continuous Integration
Testing Philosophy
Core Principles
- Test First, Code Second: Write tests before implementation
- Comprehensive Coverage: Unit, integration, and E2E tests
- Fast Feedback: Quick test execution for rapid iteration
- Maintainable Tests: Clear, focused, and easy to update
Testing Pyramid
/\
/E2E\ <- Few, high-value scenarios
/------\
/Integr. \ <- API and component integration
/----------\
/ Unit \ <- Many, fast, focused tests
/--------------\
Test-Driven Development (TDD)
Overview
Test-Driven Development is a software development approach where tests are written before the implementation code. Claude Code excels at facilitating TDD workflows through its intelligent assistance and automation capabilities.
The Red-Green-Refactor Cycle
The core of TDD is the iterative cycle:
- Red Phase: Write a failing test that defines desired functionality
- Green Phase: Write minimal production code to make the test pass
- Refactor Phase: Clean up and optimize while maintaining passing tests
// Example: Building a price calculator with TDD
// 1. RED: Start with a failing test
test('should calculate total price with tax', () => {
const result = calculateTotal(100, 0.08);
expect(result).toBe(108);
});
// 2. GREEN: Minimal implementation to pass
function calculateTotal(price: number, taxRate: number): number {
return price * (1 + taxRate);
}
// 3. REFACTOR: Improve clarity and structure
function calculateTotal(price: number, taxRate: number): number {
const taxAmount = price * taxRate;
const total = price + taxAmount;
return Number(total.toFixed(2)); // Handle floating point precision
}TDD Workflow with Claude Code
Basic TDD Commands
# Start a TDD session
claude "Implement a user authentication module using TDD. Start by writing tests for email validation."
# Continue the cycle
claude "Now implement the minimal code to make the email validation test pass"
claude "Refactor the implementation while keeping tests green"TDD Best Practices with Claude Code
- Be Explicit: Always specify βusing TDDβ to prevent Claude from creating implementations first
- Iterative Refinement: Claudeβs outputs improve with 2-3 iterations
- Preserve Tests: Instruct Claude not to modify existing tests when implementing
- Version Control: Commit tests separately from implementations
- Test Coverage: Claude generates tests with ~95% pass rate and ~85% relevance
- Edge Cases: Claude excels at identifying edge cases developers often overlook
Advanced TDD Topics
For advanced TDD patterns and automation strategies, see:
- Testing Patterns - Enterprise-scale TDD enforcement, multi-instance testing, and automation
- Testing Deep Dive - Comprehensive coverage of all testing topics including advanced TDD
Testing Strategies
1. Unit Testing
Focus: Individual functions and components in isolation
// Example: Testing a utility function
describe('formatCurrency', () => {
test('formats positive numbers', () => {
expect(formatCurrency(1234.56)).toBe('$1,234.56');
});
test('formats negative numbers', () => {
expect(formatCurrency(-1234.56)).toBe('-$1,234.56');
});
test('handles zero', () => {
expect(formatCurrency(0)).toBe('$0.00');
});
});2. Integration Testing
Focus: Component interactions and API endpoints
// Example: Testing API endpoint
describe('POST /api/users', () => {
test('creates user with valid data', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'John', email: 'john@example.com' });
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');
});
test('rejects invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'John', email: 'invalid' });
expect(response.status).toBe(400);
});
});3. End-to-End (E2E) Testing
Focus: Complete user workflows
// Example: E2E test with Playwright
test('user can complete purchase', async ({ page }) => {
await page.goto('/');
await page.click('text=Shop Now');
await page.click('[data-testid="add-to-cart"]');
await page.click('text=Checkout');
await page.fill('[name="email"]', 'test@example.com');
await page.click('text=Complete Purchase');
await expect(page.locator('text=Order Confirmed')).toBeVisible();
});Framework-Specific Guides
Jest Configuration
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts']
};Vitest Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
reporter: ['text', 'json', 'html']
}
}
});React Testing Library
// Component test example
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});Testing with Claude Code
Claude Code significantly enhances testing workflows through AI-powered assistance. Hereβs a brief overview of key capabilities:
Key Testing Features
- Multi-Agent Test Generation: Parallel test creation for comprehensive coverage
- Test Coverage Analysis: Identify and fill coverage gaps automatically
- Mock and Stub Generation: Auto-generate realistic test doubles
- Edge Case Detection: Claude excels at finding edge cases developers miss
Quick Example
// Generate comprehensive tests with Claude
claude "Generate unit tests for the authentication module with edge cases"
// Analyze and improve coverage
claude "Analyze test coverage and generate tests for uncovered paths"Learn More
For comprehensive coverage of AI-powered testing with Claude Code, including:
- Multi-agent test orchestration
- Self-healing test patterns
- Test data generation
- Visual regression testing
- CI/CD integration
See the dedicated guide: AI-Powered Test Generation Patterns
Best Practices
1. Test Organization
src/
βββ components/
β βββ Button.tsx
β βββ Button.test.tsx # Co-located tests
βββ utils/
β βββ format.ts
β βββ format.test.ts
βββ __tests__/ # Integration/E2E tests
βββ api/
βββ e2e/
2. Naming Conventions
// Descriptive test names
describe('ShoppingCart', () => {
describe('addItem', () => {
test('should add new item to empty cart', () => {});
test('should increase quantity for existing item', () => {});
test('should throw error for invalid item', () => {});
});
});3. Test Isolation
// Setup and teardown
beforeEach(() => {
// Reset database
// Clear mocks
// Initialize test state
});
afterEach(() => {
// Cleanup
// Restore mocks
});4. Assertion Best Practices
// Be specific with assertions
expect(user).toEqual({
id: expect.any(String),
name: 'John Doe',
email: 'john@example.com',
createdAt: expect.any(Date)
});
// Use appropriate matchers
expect(array).toHaveLength(3);
expect(value).toBeGreaterThan(0);
expect(promise).rejects.toThrow('Error message');5. Async Testing
// Proper async/await usage
test('fetches user data', async () => {
const user = await fetchUser('123');
expect(user.name).toBe('John');
});
// Testing async errors
test('handles fetch error', async () => {
await expect(fetchUser('invalid')).rejects.toThrow('User not found');
});Common Testing Patterns
1. Testing Hooks
// Custom hook testing
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
test('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});2. Testing Context Providers
// Wrapper for context testing
const wrapper = ({ children }) => (
<AuthProvider>
<ThemeProvider>
{children}
</ThemeProvider>
</AuthProvider>
);
test('uses auth context', () => {
const { result } = renderHook(() => useAuth(), { wrapper });
expect(result.current.user).toBeNull();
});3. Testing Error Boundaries
// Error boundary testing
test('displays error message', () => {
const ThrowError = () => {
throw new Error('Test error');
};
render(
<ErrorBoundary>
<ThrowError />
</ErrorBoundary>
);
expect(screen.getByText(/something went wrong/i)).toBeInTheDocument();
});4. Property-Based Testing
// Using fast-check for property testing
import fc from 'fast-check';
test('sort is idempotent', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const sorted = sort(arr);
const doubleSorted = sort(sorted);
expect(sorted).toEqual(doubleSorted);
})
);
});5. Snapshot Testing
// Snapshot test for component output
test('renders correctly', () => {
const component = render(<UserProfile user={mockUser} />);
expect(component).toMatchSnapshot();
});6. Test Data Builders
Note: For comprehensive test data builder patterns and AI-powered test data generation, see the Testing Patterns section.
Continuous Integration
GitHub Actions Example
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v3Related Resources
Testing Patterns (Advanced)
- Testing Patterns - Enterprise-scale testing strategies and advanced TDD automation
- AI-Powered Test Generation - Automated test creation
- Testing Deep Dive - Comprehensive testing reference