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

  1. Testing Philosophy
  2. Test-Driven Development (TDD)
  3. Testing Strategies
  4. Framework-Specific Guides
  5. Testing with Claude Code
  6. Best Practices
  7. Common Testing Patterns
  8. Continuous Integration

Testing Philosophy

Core Principles

  1. Test First, Code Second: Write tests before implementation
  2. Comprehensive Coverage: Unit, integration, and E2E tests
  3. Fast Feedback: Quick test execution for rapid iteration
  4. 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:

  1. Red Phase: Write a failing test that defines desired functionality
  2. Green Phase: Write minimal production code to make the test pass
  3. 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

  1. Be Explicit: Always specify β€œusing TDD” to prevent Claude from creating implementations first
  2. Iterative Refinement: Claude’s outputs improve with 2-3 iterations
  3. Preserve Tests: Instruct Claude not to modify existing tests when implementing
  4. Version Control: Commit tests separately from implementations
  5. Test Coverage: Claude generates tests with ~95% pass rate and ~85% relevance
  6. 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@v3

Testing Patterns (Advanced)

Documentation

External Resources

🧭 Quick Navigation

← Back to Development | Testing Patterns | Workshops