# Testing Strategy Guide

## Testing Strategy Checklist

- [ ] Unit tests cover all business logic ([Unit Testing](#unit-testing))
- [ ] Integration tests verify API contracts ([Integration Testing](#integration-testing))
- [ ] E2E tests cover critical user journeys ([End-to-End Testing](#end-to-end-testing))
- [ ] Test data is isolated and reproducible ([Test Data Management](#test-data-management))
- [ ] Async operations tested properly ([Testing Async Code](#testing-async-code))
- [ ] Error cases tested thoroughly ([Unit Test Principles](#unit-test-principles))
- [ ] Performance benchmarks in place ([Performance Testing](#performance-testing))
- [ ] Security tests included ([Testing Strategy](#testing-strategy))
- [ ] Accessibility tests implemented ([Testing Strategy](#testing-strategy))
- [ ] Visual regression tests configured ([Testing Strategy](#testing-strategy))
- [ ] Coverage metrics meet thresholds ([Test Coverage](#test-coverage))
- [ ] Tests run in parallel where possible ([Test File Organization](#test-file-organization))
- [ ] CI pipeline optimized for speed ([CI/CD Testing](#cicd-testing))
- [ ] Flaky tests identified and fixed ([E2E Test Best Practices](#e2e-test-best-practices))
- [ ] Test documentation maintained ([Test File Organization](#test-file-organization))
- [ ] AI-generated code thoroughly tested ([Testing with AI Code](#testing-with-ai-code))
- [ ] Load tests validate scalability ([Load Testing](#load-testing))
- [ ] Mutation testing enabled for critical code ([Coverage Strategies](#coverage-strategies))

## Core Philosophy

Testing is not about finding bugs, it's about building confidence. Write tests that give you the courage to refactor, deploy on Friday, and sleep soundly. Test behavior, not implementation. Make tests that fail loudly when something breaks and pass quietly when everything works.

## Testing Pyramid

### Test Distribution Strategy
Balance test types for optimal coverage and speed.

**Why:** The testing pyramid ensures fast feedback loops, maintainable tests, and cost-effective quality assurance. Too many E2E tests make suites slow and flaky; too few integration tests miss critical interactions.

```typescript
// Recommended distribution for web applications
const TEST_DISTRIBUTION = {
  unit: 70,        // Fast, isolated, numerous
  integration: 20, // Critical paths, API contracts
  e2e: 10         // User journeys, smoke tests
};

// Test execution times targets
const PERFORMANCE_TARGETS = {
  unit: {
    individual: 50,    // ms per test
    total: 30         // seconds for all unit tests
  },
  integration: {
    individual: 500,   // ms per test
    total: 120        // seconds for all integration tests
  },
  e2e: {
    individual: 5000,  // ms per test
    total: 300        // seconds for all E2E tests
  }
};

// Example test suite structure
describe('UserService', () => {
  describe('Unit Tests (70%)', () => {
    test('validates email format', () => {
      expect(validateEmail('invalid')).toBe(false);
      expect(validateEmail('user@example.com')).toBe(true);
    });
    
    test('hashes passwords correctly', async () => {
      const hashed = await hashPassword('secret123');
      expect(hashed).not.toBe('secret123');
      expect(hashed.length).toBeGreaterThan(50);
    });
  });
  
  describe('Integration Tests (20%)', () => {
    test('creates user with database', async () => {
      const user = await userService.create({
        email: 'test@example.com',
        password: 'secure123'
      });
      
      const found = await db.query('SELECT * FROM users WHERE id = $1', [user.id]);
      expect(found.rows[0].email).toBe('test@example.com');
    });
  });
  
  describe('E2E Tests (10%)', () => {
    test('user can sign up and login', async () => {
      await page.goto('/signup');
      await page.fill('[name="email"]', 'newuser@example.com');
      await page.fill('[name="password"]', 'secure123');
      await page.click('[type="submit"]');
      
      await expect(page).toHaveURL('/dashboard');
      await expect(page.locator('.welcome')).toContainText('Welcome');
    });
  });
});
```

## Test File Organization

### Directory Structure
Organize tests based on type and scope.

**Why:** Consistent test organization makes it easy to find tests, run specific suites, and understand test coverage at a glance. It also clarifies which tests to run during different stages of development.

```typescript
// Overall test organization strategy
src/
├── features/                    // Following FSD structure
│   └── auth/
│       ├── ui/
│       │   └── __tests__/      // Colocated unit tests
│       ├── api/
│       │   └── __tests__/      // Colocated API tests
│       └── __fixtures__/       // Feature-specific test data
├── widgets/
│   └── __tests__/              // Widget integration tests
├── pages/
│   └── __tests__/              // Page-level tests
├── e2e/                         // Separated E2E tests
│   ├── smoke/                  // Critical path tests
│   ├── regression/             // Full regression suite
│   └── fixtures/               // E2E test data
├── performance/                 // Separated performance tests
│   ├── load/
│   ├── stress/
│   └── benchmarks/
└── test-utils/                  // Shared test utilities
    ├── render.tsx              // Custom render functions
    ├── factories/              // Data factories
    └── mocks/                  // Shared mocks
```

### Test Colocation vs Separation Rules
When to colocate and when to separate tests.

**Why:** Colocating related tests improves maintenance, while separating certain test types optimizes CI/CD pipelines and prevents slow tests from blocking development.

```typescript
// COLOCATE: Unit and integration tests
features/user-profile/
├── model/
│   ├── profileStore.ts
│   └── __tests__/
│       ├── profileStore.test.ts          // Unit test - colocated
│       └── profileStore.integration.test.ts // Integration - colocated

// SEPARATE: E2E tests
e2e/
├── user-flows/
│   ├── signup.test.ts                    // E2E - separated
│   ├── checkout.test.ts
│   └── support/
│       └── commands.ts

// SEPARATE: Performance tests  
performance/
├── api/
│   ├── loadTest.js                       // k6 script - separated
│   └── stressTest.js
└── frontend/
    └── lighthouse.test.js

// COLOCATE: Component tests
shared/ui/DataTable/
├── DataTable.tsx
├── DataTable.styles.ts
└── __tests__/
    ├── DataTable.test.tsx                // Unit - colocated
    ├── DataTable.visual.test.tsx         // Visual - colocated
    └── __snapshots__/
        └── DataTable.test.tsx.snap

// Configuration for different test types
{
  "scripts": {
    "test:unit": "jest --testMatch='**/*.test.ts(x)?'",
    "test:integration": "jest --testMatch='**/*.integration.test.ts(x)?'",
    "test:e2e": "playwright test e2e/",
    "test:perf": "k6 run performance/",
    "test:visual": "jest --testMatch='**/*.visual.test.ts(x)?'"
  }
}
```

### Test File Naming Conventions
Consistent naming for different test types.

**Why:** Clear naming conventions enable targeted test execution, make test purpose obvious, and help with test filtering in CI/CD pipelines.

```typescript
// Standard naming patterns
*.test.ts                 // Default: unit tests
*.test.tsx                // Component unit tests
*.integration.test.ts     // Integration tests
*.e2e.test.ts            // End-to-end tests
*.perf.test.ts           // Performance tests
*.visual.test.tsx        // Visual regression tests
*.smoke.test.ts          // Smoke tests
*.contract.test.ts       // Contract tests
*.accessibility.test.tsx // A11y tests

// Special directories
__tests__/               // Test files
__fixtures__/            // Test data and factories
__mocks__/              // Manual mocks
__snapshots__/          // Jest snapshots
__image_snapshots__/    // Visual regression snapshots

// Example: Complete test structure for a feature
features/shopping-cart/
├── ui/
│   ├── CartItem.tsx
│   ├── CartSummary.tsx
│   └── __tests__/
│       ├── CartItem.test.tsx              // Unit test
│       ├── CartItem.accessibility.test.tsx // A11y test
│       ├── CartSummary.test.tsx
│       └── __snapshots__/
├── model/
│   ├── cartStore.ts
│   └── __tests__/
│       ├── cartStore.test.ts              // Unit test
│       └── cartStore.integration.test.ts  // With real API
├── api/
│   ├── cartService.ts
│   └── __tests__/
│       ├── cartService.test.ts            // Unit test
│       └── cartService.contract.test.ts   // Contract test
├── __fixtures__/
│   ├── cartFactory.ts
│   └── mockProducts.ts
└── __mocks__/
    └── api.ts
```

### Test Data Organization
Structure test data and utilities effectively.

**Why:** Well-organized test data prevents duplication, makes tests more maintainable, and ensures consistent test scenarios across different test types.

```typescript
// Feature-level fixtures (colocated)
features/auth/__fixtures__/
├── users.ts              // Test user data
├── tokens.ts             // Mock tokens
└── factories.ts          // User factory

// Shared test utilities
test-utils/
├── render.tsx            // Custom render with providers
├── factories/
│   ├── userFactory.ts    // Shared user factory
│   ├── orderFactory.ts   // Order factory
│   └── index.ts
├── mocks/
│   ├── handlers.ts       // MSW handlers
│   ├── server.ts         // MSW server setup
│   └── localStorage.ts   // Storage mocks
└── helpers/
    ├── waitFor.ts        // Async helpers
    └── assertions.ts     // Custom assertions

// E2E test fixtures (separated)
e2e/fixtures/
├── users.json            // Test user accounts
├── testData.sql          // Database seeds
└── pages/
    ├── LoginPage.ts      // Page objects
    └── CheckoutPage.ts

// Usage example
// In features/auth/__tests__/login.test.ts
import { createMockUser } from '../__fixtures__/users';
import { render } from '@/test-utils/render';

// In e2e/checkout.test.ts
import { LoginPage } from './fixtures/pages/LoginPage';
import testUsers from './fixtures/users.json';
```

### Test Environment Files
Organize environment-specific test configurations.

**Why:** Different test types need different configurations. Organizing these properly prevents configuration conflicts and makes test execution predictable.

```typescript
// Test environment files structure
config/
├── jest/
│   ├── jest.config.base.js
│   ├── jest.config.unit.js
│   ├── jest.config.integration.js
│   └── setupTests.ts
├── playwright/
│   ├── playwright.config.ts
│   └── global-setup.ts
└── test-environments/
    ├── .env.test             // Unit/integration tests
    ├── .env.e2e              // E2E tests
    └── .env.ci               // CI-specific settings

// Example jest.config.unit.js
module.exports = {
  ...require('./jest.config.base'),
  testMatch: ['**/*.test.ts(x)?'],
  testPathIgnorePatterns: [
    '/node_modules/',
    '/*.integration.test.ts(x)?$/',
    '/e2e/',
    '/performance/'
  ],
  setupFilesAfterEnv: ['<rootDir>/config/jest/setupTests.ts']
};

// Example playwright.config.ts
export default {
  testDir: './e2e',
  testMatch: '**/*.e2e.test.ts',
  outputDir: './test-results',
  use: {
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    trace: 'on-first-retry'
  }
};
```

### CI/CD Test Organization
Optimize test organization for CI pipelines.

**Why:** CI/CD pipelines have different requirements than local development. Organizing tests for parallel execution and selective running speeds up pipelines and provides faster feedback.

```typescript
// Organized by execution speed and priority
.github/workflows/ci.yml
├── tier-1-fast/           # < 2 minutes
│   ├── lint
│   ├── type-check
│   └── unit-tests
├── tier-2-integration/    # < 5 minutes
│   ├── api-tests
│   └── db-tests
├── tier-3-e2e/           # < 15 minutes
│   ├── smoke-tests
│   └── critical-paths
└── tier-4-full/          # Nightly/deployment
    ├── full-e2e
    ├── performance
    └── visual-regression

// Test tagging for selective execution
// features/auth/__tests__/auth.test.ts
/**
 * @jest-environment jsdom
 * @tags unit, auth, critical
 */
describe('Authentication', () => {
  // Can run with: jest --selectTags=critical
});

// Shard configuration for parallel execution
// jest.config.js
module.exports = {
  projects: [
    {
      displayName: 'Features',
      testMatch: ['<rootDir>/src/features/**/*.test.ts']
    },
    {
      displayName: 'UI Components',
      testMatch: ['<rootDir>/src/shared/ui/**/*.test.tsx']
    },
    {
      displayName: 'API',
      testMatch: ['<rootDir>/src/api/**/*.test.ts']
    }
  ]
};
```

## Unit Testing

### Unit Test Principles
Write fast, focused, isolated tests.

**Why:** Unit tests provide instant feedback during development, make refactoring safe, serve as living documentation, and pinpoint exactly what broke when they fail.

```typescript
// Good unit test characteristics
describe('PriceCalculator', () => {
  // 1. Test one thing
  test('applies percentage discount correctly', () => {
    const result = calculatePrice(100, { type: 'percentage', value: 10 });
    expect(result).toBe(90);
  });
  
  // 2. Descriptive names
  test('throws error when discount exceeds 100%', () => {
    expect(() => {
      calculatePrice(100, { type: 'percentage', value: 150 });
    }).toThrow('Discount cannot exceed 100%');
  });
  
  // 3. Arrange-Act-Assert pattern
  test('applies bulk discount for large quantities', () => {
    // Arrange
    const basePrice = 10;
    const quantity = 100;
    const bulkThreshold = 50;
    
    // Act
    const total = calculateBulkPrice(basePrice, quantity, bulkThreshold);
    
    // Assert
    expect(total).toBe(900); // 10% bulk discount
  });
  
  // 4. Test edge cases
  describe('edge cases', () => {
    test('handles zero price', () => {
      expect(calculatePrice(0, { type: 'fixed', value: 10 })).toBe(0);
    });
    
    test('handles negative quantities gracefully', () => {
      expect(() => calculateBulkPrice(10, -1)).toThrow('Quantity must be positive');
    });
    
    test('handles maximum safe integer', () => {
      expect(calculatePrice(Number.MAX_SAFE_INTEGER, { type: 'fixed', value: 1 }))
        .toBe(Number.MAX_SAFE_INTEGER - 1);
    });
  });
});

// Test doubles (mocks, stubs, spies)
describe('EmailService', () => {
  let mockTransporter: jest.MockedObject<Transporter>;
  
  beforeEach(() => {
    mockTransporter = {
      sendMail: jest.fn().mockResolvedValue({ messageId: '123' })
    };
  });
  
  test('sends welcome email with correct template', async () => {
    const emailService = new EmailService(mockTransporter);
    
    await emailService.sendWelcomeEmail('user@example.com', 'John');
    
    expect(mockTransporter.sendMail).toHaveBeenCalledWith({
      to: 'user@example.com',
      subject: 'Welcome to MyApp',
      template: 'welcome',
      context: { name: 'John' }
    });
  });
  
  test('retries on transient failures', async () => {
    mockTransporter.sendMail
      .mockRejectedValueOnce(new Error('Network error'))
      .mockResolvedValueOnce({ messageId: '456' });
    
    const emailService = new EmailService(mockTransporter);
    const result = await emailService.sendWelcomeEmail('user@example.com', 'John');
    
    expect(mockTransporter.sendMail).toHaveBeenCalledTimes(2);
    expect(result.messageId).toBe('456');
  });
});

// Testing pure functions
describe('Data Transformers', () => {
  test('normalizes phone numbers', () => {
    const testCases = [
      { input: '(555) 123-4567', expected: '+15551234567' },
      { input: '555.123.4567', expected: '+15551234567' },
      { input: '555 123 4567', expected: '+15551234567' },
      { input: '+1-555-123-4567', expected: '+15551234567' }
    ];
    
    testCases.forEach(({ input, expected }) => {
      expect(normalizePhoneNumber(input)).toBe(expected);
    });
  });
});
```

### Testing Async Code
Handle promises and callbacks correctly.

**Why:** Async bugs are hard to debug in production. Proper async testing ensures your code handles both success and failure cases, including timeouts and race conditions.

```typescript
// Testing promises
describe('Async Operations', () => {
  // Using async/await
  test('fetches user data successfully', async () => {
    const user = await fetchUser('123');
    expect(user.name).toBe('John Doe');
  });
  
  // Testing rejections
  test('handles API errors gracefully', async () => {
    await expect(fetchUser('invalid')).rejects.toThrow('User not found');
  });
  
  // Testing with done callback (avoid when possible)
  test('emits event on completion', (done) => {
    const emitter = processData();
    
    emitter.on('complete', (result) => {
      expect(result).toBe('processed');
      done();
    });
  });
  
  // Testing timeouts
  test('times out after 5 seconds', async () => {
    const slowOperation = new Promise((resolve) => {
      setTimeout(resolve, 6000);
    });
    
    await expect(
      withTimeout(slowOperation, 5000)
    ).rejects.toThrow('Operation timed out');
  }, 10000); // Jest timeout
  
  // Testing race conditions
  test('handles concurrent updates correctly', async () => {
    const updates = Array(10).fill(null).map((_, i) => 
      updateCounter(i)
    );
    
    await Promise.all(updates);
    
    const finalValue = await getCounter();
    expect(finalValue).toBe(10); // Not 1 or random number
  });
  
  // Testing event emitters
  test('emits events in correct order', async () => {
    const events: string[] = [];
    const processor = new DataProcessor();
    
    processor.on('start', () => events.push('start'));
    processor.on('process', () => events.push('process'));
    processor.on('complete', () => events.push('complete'));
    
    await processor.run();
    
    expect(events).toEqual(['start', 'process', 'complete']);
  });
});

// Testing with fake timers
describe('Scheduled Operations', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });
  
  afterEach(() => {
    jest.useRealTimers();
  });
  
  test('retries with exponential backoff', async () => {
    const operation = jest.fn()
      .mockRejectedValueOnce(new Error('Fail 1'))
      .mockRejectedValueOnce(new Error('Fail 2'))
      .mockResolvedValueOnce('Success');
    
    const promise = retryWithBackoff(operation);
    
    // Fast-forward through retries
    jest.advanceTimersByTime(1000); // First retry
    await Promise.resolve();
    
    jest.advanceTimersByTime(2000); // Second retry (backoff)
    await Promise.resolve();
    
    const result = await promise;
    expect(result).toBe('Success');
    expect(operation).toHaveBeenCalledTimes(3);
  });
});
```

## Integration Testing

### API Integration Tests
Test API endpoints with real HTTP requests.

**Why:** API tests verify that your endpoints work correctly with all middleware, validation, and database interactions. They catch issues unit tests miss while being faster than E2E tests.

```typescript
// API testing setup
import request from 'supertest';
import { app } from '../app';
import { seedDatabase, cleanDatabase } from './helpers';

describe('User API', () => {
  beforeEach(async () => {
    await cleanDatabase();
    await seedDatabase();
  });
  
  describe('POST /api/users', () => {
    test('creates user with valid data', async () => {
      const response = await request(app)
        .post('/api/users')
        .send({
          email: 'newuser@example.com',
          password: 'secure123',
          name: 'New User'
        })
        .expect(201)
        .expect('Content-Type', /json/);
      
      expect(response.body).toMatchObject({
        success: true,
        data: {
          id: expect.any(String),
          email: 'newuser@example.com',
          name: 'New User'
        }
      });
      
      // Verify in database
      const user = await db.query(
        'SELECT * FROM users WHERE email = $1',
        ['newuser@example.com']
      );
      expect(user.rows).toHaveLength(1);
    });
    
    test('validates required fields', async () => {
      const response = await request(app)
        .post('/api/users')
        .send({ email: 'invalid-email' })
        .expect(422);
      
      expect(response.body).toMatchObject({
        success: false,
        error: {
          code: 'VALIDATION_ERROR',
          details: expect.arrayContaining([
            expect.objectContaining({
              field: 'email',
              message: expect.stringContaining('valid email')
            }),
            expect.objectContaining({
              field: 'password',
              message: expect.stringContaining('required')
            })
          ])
        }
      });
    });
    
    test('prevents duplicate emails', async () => {
      // Create first user
      await request(app)
        .post('/api/users')
        .send({
          email: 'duplicate@example.com',
          password: 'secure123'
        })
        .expect(201);
      
      // Attempt duplicate
      const response = await request(app)
        .post('/api/users')
        .send({
          email: 'duplicate@example.com',
          password: 'different123'
        })
        .expect(409);
      
      expect(response.body.error.code).toBe('EMAIL_EXISTS');
    });
  });
  
  describe('GET /api/users/:id', () => {
    test('returns user when authenticated', async () => {
      const token = await getAuthToken('user@example.com', 'password');
      
      const response = await request(app)
        .get('/api/users/123')
        .set('Authorization', `Bearer ${token}`)
        .expect(200);
      
      expect(response.body.data).toHaveProperty('email');
      expect(response.body.data).not.toHaveProperty('password');
    });
    
    test('returns 401 without authentication', async () => {
      await request(app)
        .get('/api/users/123')
        .expect(401);
    });
    
    test('returns 403 for other users data', async () => {
      const token = await getAuthToken('user1@example.com', 'password');
      
      await request(app)
        .get('/api/users/other-user-id')
        .set('Authorization', `Bearer ${token}`)
        .expect(403);
    });
  });
  
  describe('Rate limiting', () => {
    test('enforces rate limits', async () => {
      const requests = Array(101).fill(null).map(() => 
        request(app).get('/api/users')
      );
      
      const responses = await Promise.all(requests);
      const tooManyRequests = responses.filter(r => r.status === 429);
      
      expect(tooManyRequests.length).toBeGreaterThan(0);
      expect(tooManyRequests[0].headers).toHaveProperty('x-ratelimit-limit');
      expect(tooManyRequests[0].headers).toHaveProperty('retry-after');
    });
  });
});

// Database integration tests
describe('Database Operations', () => {
  test('transaction rollback on error', async () => {
    const initialCount = await getUserCount();
    
    try {
      await db.transaction(async (trx) => {
        await trx.query('INSERT INTO users (email) VALUES ($1)', ['user1@example.com']);
        await trx.query('INSERT INTO users (email) VALUES ($1)', ['user1@example.com']); // Duplicate
      });
    } catch (error) {
      // Expected to fail
    }
    
    const finalCount = await getUserCount();
    expect(finalCount).toBe(initialCount); // No users added
  });
  
  test('connection pool handling', async () => {
    const queries = Array(20).fill(null).map(() => 
      db.query('SELECT 1')
    );
    
    const results = await Promise.all(queries);
    expect(results).toHaveLength(20);
    
    const poolStats = await db.pool.getStats();
    expect(poolStats.idle + poolStats.busy).toBeLessThanOrEqual(10); // Max pool size
  });
});
```

### Component Integration Tests
Test component interactions and data flow.

**Why:** Component integration tests verify that your UI components work together correctly, handling props, events, and state changes. They catch issues that unit tests miss without the complexity of E2E tests.

```typescript
// React component integration tests
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserDashboard } from '../UserDashboard';
import { server } from './mocks/server';

describe('UserDashboard Integration', () => {
  beforeAll(() => server.listen());
  afterEach(() => server.resetHandlers());
  afterAll(() => server.close());
  
  test('loads and displays user data', async () => {
    render(<UserDashboard userId="123" />);
    
    // Loading state
    expect(screen.getByText(/loading/i)).toBeInTheDocument();
    
    // Data loaded
    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('john@example.com')).toBeInTheDocument();
    });
  });
  
  test('handles user interactions', async () => {
    const user = userEvent.setup();
    render(<UserDashboard userId="123" />);
    
    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });
    
    // Click edit button
    await user.click(screen.getByRole('button', { name: /edit/i }));
    
    // Edit form appears
    expect(screen.getByRole('textbox', { name: /name/i })).toHaveValue('John Doe');
    
    // Change name
    await user.clear(screen.getByRole('textbox', { name: /name/i }));
    await user.type(screen.getByRole('textbox', { name: /name/i }), 'Jane Doe');
    
    // Submit
    await user.click(screen.getByRole('button', { name: /save/i }));
    
    // Verify update
    await waitFor(() => {
      expect(screen.getByText('Jane Doe')).toBeInTheDocument();
      expect(screen.getByText(/profile updated/i)).toBeInTheDocument();
    });
  });
  
  test('handles error states', async () => {
    server.use(
      rest.get('/api/users/:id', (req, res, ctx) => {
        return res(ctx.status(500), ctx.json({ error: 'Server error' }));
      })
    );
    
    render(<UserDashboard userId="123" />);
    
    await waitFor(() => {
      expect(screen.getByText(/error loading user/i)).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /retry/i })).toBeInTheDocument();
    });
  });
});

// Testing with React Testing Library best practices
describe('Form Component', () => {
  test('submits with valid data', async () => {
    const handleSubmit = jest.fn();
    const user = userEvent.setup();
    
    render(<ContactForm onSubmit={handleSubmit} />);
    
    // Fill form using accessible queries
    await user.type(screen.getByLabelText(/email/i), 'user@example.com');
    await user.type(screen.getByLabelText(/message/i), 'Hello world');
    
    // Submit
    await user.click(screen.getByRole('button', { name: /send/i }));
    
    await waitFor(() => {
      expect(handleSubmit).toHaveBeenCalledWith({
        email: 'user@example.com',
        message: 'Hello world'
      });
    });
  });
  
  test('shows validation errors', async () => {
    const user = userEvent.setup();
    render(<ContactForm onSubmit={jest.fn()} />);
    
    // Submit without filling
    await user.click(screen.getByRole('button', { name: /send/i }));
    
    expect(screen.getByRole('alert')).toHaveTextContent(/email is required/i);
    expect(screen.getByRole('alert')).toHaveTextContent(/message is required/i);
  });
});
```

## End-to-End Testing

### E2E Test Strategy
Test critical user journeys.

**Why:** E2E tests verify that your entire application works from the user's perspective, catching integration issues between frontend, backend, and external services that other tests miss.

```typescript
// Playwright E2E tests
import { test, expect } from '@playwright/test';
import { createTestUser, deleteTestUser } from './helpers';

test.describe('User Journey', () => {
  let testUser: TestUser;
  
  test.beforeEach(async () => {
    testUser = await createTestUser();
  });
  
  test.afterEach(async () => {
    await deleteTestUser(testUser.id);
  });
  
  test('complete purchase flow', async ({ page, context }) => {
    // Login
    await page.goto('/login');
    await page.fill('[data-testid="email-input"]', testUser.email);
    await page.fill('[data-testid="password-input"]', testUser.password);
    await page.click('[data-testid="login-button"]');
    
    await expect(page).toHaveURL('/dashboard');
    
    // Browse products
    await page.click('text=Products');
    await expect(page.locator('.product-card')).toHaveCount(10);
    
    // Add to cart
    await page.click('.product-card:first-child >> text=Add to Cart');
    await expect(page.locator('.cart-count')).toHaveText('1');
    
    // Checkout
    await page.click('[data-testid="cart-icon"]');
    await page.click('text=Checkout');
    
    // Fill payment details
    await page.fill('[data-testid="card-number"]', '4242424242424242');
    await page.fill('[data-testid="card-expiry"]', '12/25');
    await page.fill('[data-testid="card-cvc"]', '123');
    
    // Complete purchase
    await page.click('[data-testid="place-order-button"]');
    
    // Confirmation
    await expect(page.locator('.order-success')).toBeVisible();
    await expect(page.locator('.order-number')).toContainText(/ORDER-\d+/);
    
    // Verify email sent (using API)
    const emails = await getTestEmails(testUser.email);
    expect(emails).toContainEqual(
      expect.objectContaining({
        subject: expect.stringContaining('Order Confirmation')
      })
    );
  });
  
  test('handles payment failure gracefully', async ({ page }) => {
    await loginAs(page, testUser);
    await addProductToCart(page);
    await page.goto('/checkout');
    
    // Use card that triggers failure
    await page.fill('[data-testid="card-number"]', '4000000000000002');
    await page.fill('[data-testid="card-expiry"]', '12/25');
    await page.fill('[data-testid="card-cvc"]', '123');
    
    await page.click('[data-testid="place-order-button"]');
    
    // Error handling
    await expect(page.locator('.error-message')).toContainText(/payment declined/i);
    await expect(page).toHaveURL('/checkout'); // Stays on checkout
    
    // Can retry with different card
    await page.fill('[data-testid="card-number"]', '4242424242424242');
    await page.click('[data-testid="place-order-button"]');
    
    await expect(page.locator('.order-success')).toBeVisible();
  });
});

// Mobile E2E tests
test.describe('Mobile Experience', () => {
  test.use({
    viewport: { width: 375, height: 667 },
    isMobile: true
  });
  
  test('responsive navigation', async ({ page }) => {
    await page.goto('/');
    
    // Hamburger menu visible
    await expect(page.locator('[data-testid="mobile-menu"]')).toBeVisible();
    await expect(page.locator('.desktop-nav')).not.toBeVisible();
    
    // Open mobile menu
    await page.click('[data-testid="mobile-menu"]');
    await expect(page.locator('.mobile-nav-drawer')).toBeVisible();
    
    // Navigate
    await page.click('.mobile-nav-drawer >> text=Products');
    await expect(page).toHaveURL('/products');
    await expect(page.locator('.mobile-nav-drawer')).not.toBeVisible();
  });
});

// Cross-browser testing
['chromium', 'firefox', 'webkit'].forEach(browserName => {
  test.describe(`Cross-browser: ${browserName}`, () => {
    test.use({ browserName });
    
    test('core functionality works', async ({ page }) => {
      await page.goto('/');
      await expect(page.locator('h1')).toContainText('Welcome');
      
      // Test browser-specific features
      if (browserName === 'webkit') {
        // Safari-specific test
        await expect(page.locator('.safari-specific-feature')).toBeVisible();
      }
    });
  });
});

// Visual regression tests
test('visual regression', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixels: 100,
    threshold: 0.2
  });
  
  await page.click('text=Features');
  await expect(page.locator('.features-section')).toHaveScreenshot('features.png');
});
```

### E2E Test Best Practices
Write maintainable E2E tests.

**Why:** E2E tests are expensive to maintain. Following best practices reduces flakiness, speeds up execution, and makes tests resilient to UI changes.

```typescript
// Page Object Model
class LoginPage {
  constructor(private page: Page) {}
  
  async navigate() {
    await this.page.goto('/login');
  }
  
  async login(email: string, password: string) {
    await this.page.fill('[data-testid="email-input"]', email);
    await this.page.fill('[data-testid="password-input"]', password);
    await this.page.click('[data-testid="login-button"]');
  }
  
  async expectError(message: string) {
    await expect(this.page.locator('.error-message')).toContainText(message);
  }
  
  async expectSuccess() {
    await expect(this.page).toHaveURL('/dashboard');
  }
}

// Use data-testid for stable selectors
test('login flow with page objects', async ({ page }) => {
  const loginPage = new LoginPage(page);
  
  await loginPage.navigate();
  await loginPage.login('user@example.com', 'wrongpassword');
  await loginPage.expectError('Invalid credentials');
  
  await loginPage.login('user@example.com', 'correctpassword');
  await loginPage.expectSuccess();
});

// Avoid hard-coded waits
// ❌ BAD
await page.wait(5000); // Arbitrary wait

// ✅ GOOD
await page.waitForSelector('.content', { state: 'visible' });
await expect(page.locator('.spinner')).not.toBeVisible();

// Handle flaky tests
test('flaky test with retry', async ({ page }) => {
  test.slow(); // Triple the timeout
  
  // Retry specific actions
  await expect(async () => {
    await page.click('.sometimes-slow-button');
    await expect(page.locator('.result')).toBeVisible();
  }).toPass({
    intervals: [1000, 2000, 5000],
    timeout: 30000
  });
});

// Test data isolation
test.describe('Parallel tests', () => {
  test.describe.configure({ mode: 'parallel' });
  
  test('test 1', async ({ page }) => {
    const uniqueEmail = `test-${Date.now()}-1@example.com`;
    // Use unique data
  });
  
  test('test 2', async ({ page }) => {
    const uniqueEmail = `test-${Date.now()}-2@example.com`;
    // Use different unique data
  });
});
```

## Test Data Management

### Test Data Strategies
Manage test data effectively.

**Why:** Consistent, isolated test data prevents flaky tests, enables parallel execution, and makes tests predictable. Poor test data management is the leading cause of intermittent test failures.

```typescript
// Factory pattern for test data
class UserFactory {
  private counter = 0;
  
  create(overrides: Partial<User> = {}): User {
    this.counter++;
    
    return {
      id: `test-user-${this.counter}`,
      email: `test${this.counter}@example.com`,
      name: `Test User ${this.counter}`,
      password: 'Test123!',
      createdAt: new Date(),
      ...overrides
    };
  }
  
  createMany(count: number, overrides: Partial<User> = {}): User[] {
    return Array(count).fill(null).map(() => this.create(overrides));
  }
  
  async createAndSave(overrides: Partial<User> = {}): Promise<User> {
    const user = this.create(overrides);
    await db.insert('users', user);
    return user;
  }
}

// Builder pattern for complex objects
class OrderBuilder {
  private order: Partial<Order> = {};
  
  withUser(userId: string): this {
    this.order.userId = userId;
    return this;
  }
  
  withItems(items: OrderItem[]): this {
    this.order.items = items;
    this.order.total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    return this;
  }
  
  withStatus(status: OrderStatus): this {
    this.order.status = status;
    return this;
  }
  
  build(): Order {
    return {
      id: generateId(),
      userId: this.order.userId || 'default-user',
      items: this.order.items || [],
      total: this.order.total || 0,
      status: this.order.status || 'pending',
      createdAt: new Date()
    };
  }
}

// Seeding strategies
class TestSeeder {
  async seedMinimal() {
    // Minimum data for app to function
    await this.createAdminUser();
    await this.createDefaultCategories();
  }
  
  async seedStandard() {
    await this.seedMinimal();
    
    // Typical test scenario data
    const users = await this.createUsers(5);
    await this.createProductsForEachCategory(10);
    await this.createOrdersForUsers(users, 3);
  }
  
  async seedStress() {
    await this.seedMinimal();
    
    // Large dataset for performance testing
    await this.createUsers(1000);
    await this.createProducts(10000);
    await this.createOrders(50000);
  }
  
  async cleanup() {
    // Clean in reverse order of foreign keys
    await db.query('DELETE FROM order_items');
    await db.query('DELETE FROM orders');
    await db.query('DELETE FROM products');
    await db.query('DELETE FROM users WHERE email LIKE $1', ['test%@example.com']);
  }
}

// Database snapshots
class DatabaseSnapshot {
  async create(name: string): Promise<void> {
    await db.query(`SAVEPOINT ${name}`);
  }
  
  async restore(name: string): Promise<void> {
    await db.query(`ROLLBACK TO SAVEPOINT ${name}`);
  }
  
  async createSchema(): Promise<void> {
    // Save schema state for faster restoration
    const schema = await db.query(`
      SELECT tablename 
      FROM pg_tables 
      WHERE schemaname = 'public'
    `);
    
    await fs.writeFile(
      `./test-snapshots/schema-${Date.now()}.json`,
      JSON.stringify(schema.rows)
    );
  }
}

// Test data cleanup strategies
describe('Data Cleanup', () => {
  // Strategy 1: Transaction rollback (fastest)
  test('with transaction rollback', async () => {
    await db.query('BEGIN');
    
    try {
      const user = await createUser();
      const result = await performOperation(user.id);
      expect(result).toBe('success');
    } finally {
      await db.query('ROLLBACK');
    }
  });
  
  // Strategy 2: Explicit cleanup
  test('with explicit cleanup', async () => {
    const cleanup: Array<() => Promise<void>> = [];
    
    try {
      const user = await createUser();
      cleanup.push(() => deleteUser(user.id));
      
      const order = await createOrder(user.id);
      cleanup.push(() => deleteOrder(order.id));
      
      expect(order.status).toBe('pending');
    } finally {
      // Clean up in reverse order
      for (const cleanupFn of cleanup.reverse()) {
        await cleanupFn();
      }
    }
  });
  
  // Strategy 3: Tagged data
  test('with tagged cleanup', async () => {
    const testId = `test-${Date.now()}`;
    
    await createUser({ testId });
    await createOrder({ testId });
    
    // Cleanup all data with this test ID
    await db.query('DELETE FROM orders WHERE test_id = $1', [testId]);
    await db.query('DELETE FROM users WHERE test_id = $1', [testId]);
  });
});
```

## Performance Testing

### Load Testing
Test system behavior under load.

**Why:** Load testing reveals performance bottlenecks, memory leaks, and scalability limits before they affect real users. It validates that your system meets performance requirements.

```typescript
// k6 load test script
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// Custom metrics
const errorRate = new Rate('errors');
const apiDuration = new Trend('api_duration');

// Test configuration
export const options = {
  stages: [
    { duration: '2m', target: 10 },   // Ramp up to 10 users
    { duration: '5m', target: 10 },   // Stay at 10 users
    { duration: '2m', target: 50 },   // Ramp up to 50 users
    { duration: '5m', target: 50 },   // Stay at 50 users
    { duration: '2m', target: 100 },  // Ramp up to 100 users
    { duration: '5m', target: 100 },  // Stay at 100 users
    { duration: '5m', target: 0 },    // Ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
    errors: ['rate<0.1'],              // Error rate under 10%
    http_req_failed: ['rate<0.05']     // Failed requests under 5%
  }
};

export default function () {
  // Login
  const loginRes = http.post(
    'https://api.example.com/login',
    JSON.stringify({
      email: 'loadtest@example.com',
      password: 'test123'
    }),
    { headers: { 'Content-Type': 'application/json' } }
  );
  
  check(loginRes, {
    'login successful': (r) => r.status === 200,
    'token received': (r) => Boolean(r.json('token'))
  });
  
  errorRate.add(loginRes.status !== 200);
  
  if (loginRes.status === 200) {
    const token = loginRes.json('token');
    const headers = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
    
    // Browse products
    const productsRes = http.get('https://api.example.com/products', { headers });
    apiDuration.add(productsRes.timings.duration);
    
    check(productsRes, {
      'products fetched': (r) => r.status === 200,
      'products array': (r) => Array.isArray(r.json('data'))
    });
    
    sleep(1); // User think time
    
    // Create order
    const orderRes = http.post(
      'https://api.example.com/orders',
      JSON.stringify({
        productId: 'product-1',
        quantity: 1
      }),
      { headers }
    );
    
    check(orderRes, {
      'order created': (r) => r.status === 201
    });
    
    errorRate.add(orderRes.status !== 201);
  }
  
  sleep(1);
}

// Spike test
export const spikeTest = {
  stages: [
    { duration: '10s', target: 0 },    // Baseline
    { duration: '10s', target: 1000 }, // Spike to 1000 users
    { duration: '1m', target: 1000 },  // Stay at spike
    { duration: '10s', target: 0 },    // Recovery
  ],
  thresholds: {
    http_req_duration: ['p(95)<1000'], // Degraded but functional
    http_req_failed: ['rate<0.5']      // Some failures acceptable
  }
};

// Stress test to find breaking point
export const stressTest = {
  stages: [
    { duration: '2m', target: 100 },
    { duration: '2m', target: 200 },
    { duration: '2m', target: 300 },
    { duration: '2m', target: 400 },
    { duration: '2m', target: 500 }, // Find breaking point
  ],
  thresholds: {
    http_req_duration: ['p(99)<2000'] // Maximum acceptable response time
  }
};
```

### Benchmark Testing
Measure and track performance metrics.

**Why:** Benchmarks prevent performance regression, provide data for optimization decisions, and ensure consistent performance across releases.

```typescript
// Performance benchmarks with Jest
describe('Performance Benchmarks', () => {
  test('database query performance', async () => {
    const iterations = 1000;
    const times: number[] = [];
    
    for (let i = 0; i < iterations; i++) {
      const start = performance.now();
      
      await db.query(
        'SELECT * FROM users WHERE email = $1',
        [`user${i}@example.com`]
      );
      
      times.push(performance.now() - start);
    }
    
    const average = times.reduce((a, b) => a + b) / times.length;
    const p95 = times.sort((a, b) => a - b)[Math.floor(iterations * 0.95)];
    const p99 = times.sort((a, b) => a - b)[Math.floor(iterations * 0.99)];
    
    console.log(`Query Performance:
      Average: ${average.toFixed(2)}ms
      P95: ${p95.toFixed(2)}ms
      P99: ${p99.toFixed(2)}ms
    `);
    
    expect(average).toBeLessThan(10);   // Average under 10ms
    expect(p95).toBeLessThan(20);       // P95 under 20ms
    expect(p99).toBeLessThan(50);       // P99 under 50ms
  });
  
  test('API endpoint latency', async () => {
    const results = await autocannon({
      url: 'http://localhost:3000/api/products',
      connections: 10,
      duration: 10,
      pipelining: 1,
      headers: {
        'Authorization': 'Bearer test-token'
      }
    });
    
    expect(results.latency.mean).toBeLessThan(100);
    expect(results.latency.p99).toBeLessThan(500);
    expect(results.requests.average).toBeGreaterThan(100); // RPS
  });
  
  test('memory usage stays stable', async () => {
    const initialMemory = process.memoryUsage().heapUsed;
    const measurements: number[] = [];
    
    // Perform operations
    for (let i = 0; i < 1000; i++) {
      await processLargeDataset();
      
      if (i % 100 === 0) {
        global.gc(); // Force garbage collection
        measurements.push(process.memoryUsage().heapUsed);
      }
    }
    
    const finalMemory = process.memoryUsage().heapUsed;
    const memoryGrowth = finalMemory - initialMemory;
    
    // Check for memory leaks
    expect(memoryGrowth).toBeLessThan(50 * 1024 * 1024); // Less than 50MB growth
    
    // Check stability (no continuous growth)
    const trend = calculateTrend(measurements);
    expect(trend).toBeLessThan(0.1); // Minimal upward trend
  });
});

// Frontend performance testing
test('page load performance', async ({ page }) => {
  const metrics = await page.evaluate(() => {
    const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
    
    return {
      domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
      loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
      firstPaint: performance.getEntriesByName('first-paint')[0]?.startTime,
      firstContentfulPaint: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
      largestContentfulPaint: new PerformanceObserver((list) => {
        const entries = list.getEntries();
        return entries[entries.length - 1].startTime;
      })
    };
  });
  
  expect(metrics.firstContentfulPaint).toBeLessThan(1500);
  expect(metrics.largestContentfulPaint).toBeLessThan(2500);
  expect(metrics.domContentLoaded).toBeLessThan(3000);
});
```

## Test Coverage

### Coverage Strategies
Measure and improve test coverage.

**Why:** Coverage metrics identify untested code, but 100% coverage doesn't guarantee quality. Focus on critical path coverage and mutation testing for true confidence.

```typescript
// Jest coverage configuration
module.exports = {
  collectCoverage: true,
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html'],
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/**/*.stories.tsx',
    '!src/**/index.ts',
    '!src/test/**'
  ],
  coverageThresholds: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    },
    './src/utils/': {
      branches: 90,  // Higher coverage for utilities
      functions: 90,
      lines: 90
    },
    './src/api/': {
      branches: 85,  // Critical API code
      functions: 85,
      lines: 85
    }
  }
};

// Coverage reporting
describe('Coverage Gaps', () => {
  test('identifies uncovered branches', () => {
    function processStatus(status: string): string {
      if (status === 'active') {
        return 'processing';
      } else if (status === 'pending') {
        return 'waiting';
      } else if (status === 'error') {  // Not covered
        return 'failed';
      }
      return 'unknown';  // Not covered
    }
    
    expect(processStatus('active')).toBe('processing');
    expect(processStatus('pending')).toBe('waiting');
    // Missing: error and default cases
  });
});

// Mutation testing configuration (Stryker)
module.exports = {
  mutate: [
    'src/**/*.ts',
    '!src/**/*.test.ts'
  ],
  mutator: {
    name: 'typescript',
    excludedMutations: ['StringLiteral', 'ArrayDeclaration']
  },
  testRunner: 'jest',
  jest: {
    configFile: 'jest.config.js',
    enableFindRelatedTests: true
  },
  thresholds: {
    high: 80,
    low: 70,
    break: 60  // Fail if mutation score < 60%
  },
  dashboard: {
    project: 'github.com/myorg/myproject'
  }
};

// Critical path coverage analysis
class CoverageAnalyzer {
  identifyCriticalPaths(): string[] {
    return [
      'src/api/auth/*',      // Authentication
      'src/api/payment/*',   // Payment processing
      'src/utils/crypto/*',  // Security functions
      'src/services/user/*'  // User management
    ];
  }
  
  async analyzeCriticalCoverage(): Promise<CoverageReport> {
    const criticalPaths = this.identifyCriticalPaths();
    const coverage = await this.getCoverage();
    
    const criticalCoverage = criticalPaths.map(path => ({
      path,
      lines: coverage[path]?.lines || 0,
      branches: coverage[path]?.branches || 0,
      functions: coverage[path]?.functions || 0,
      statements: coverage[path]?.statements || 0
    }));
    
    const failures = criticalCoverage.filter(c => 
      c.lines < 95 || c.branches < 90
    );
    
    if (failures.length > 0) {
      throw new Error(`Critical paths have insufficient coverage: ${
        failures.map(f => f.path).join(', ')
      }`);
    }
    
    return { criticalCoverage, overall: coverage };
  }
}
```

## Testing with AI Code

### AI-Generated Code Testing
Special considerations for AI-generated code.

**Why:** AI-generated code requires extra scrutiny because it may contain subtle bugs, security vulnerabilities, or logic errors that look correct but behave incorrectly.

```typescript
// Testing strategy for AI-generated code
class AICodeTester {
  // Property-based testing for AI functions
  async testAIGeneratedFunction(fn: Function, properties: PropertySpec[]) {
    const fc = require('fast-check');
    
    for (const prop of properties) {
      await fc.assert(
        fc.property(
          ...prop.inputs,
          (...args) => {
            const result = fn(...args);
            return prop.predicate(result, ...args);
          }
        ),
        { numRuns: 1000 }  // More runs for AI code
      );
    }
  }
  
  // Fuzz testing
  async fuzzTest(endpoint: string) {
    const fuzzer = new Fuzzer({
      target: endpoint,
      wordlist: ['../sql-injection.txt', '../xss-payloads.txt'],
      iterations: 10000
    });
    
    const vulnerabilities = await fuzzer.run();
    expect(vulnerabilities).toHaveLength(0);
  }
  
  // Regression testing for AI modifications
  async testAIModification(
    original: Function,
    modified: Function,
    testCases: TestCase[]
  ) {
    const regressions: string[] = [];
    
    for (const testCase of testCases) {
      const originalResult = original(...testCase.input);
      const modifiedResult = modified(...testCase.input);
      
      if (!deepEqual(originalResult, modifiedResult)) {
        regressions.push(testCase.description);
      }
    }
    
    if (regressions.length > 0) {
      console.warn('Regressions detected:', regressions);
    }
  }
}

// Contract testing for AI-generated APIs
describe('AI API Contract', () => {
  const contract = {
    request: {
      method: 'POST',
      path: '/api/ai/generate',
      headers: {
        'Content-Type': 'application/json'
      },
      body: {
        prompt: expect.any(String),
        maxTokens: expect.any(Number),
        temperature: expect.any(Number)
      }
    },
    response: {
      status: 200,
      body: {
        success: true,
        data: {
          id: expect.stringMatching(/^[a-z0-9-]+$/),
          content: expect.any(String),
          tokens: expect.any(Number),
          model: expect.stringMatching(/^gpt-|claude-/)
        }
      }
    }
  };
  
  test('AI endpoint matches contract', async () => {
    const response = await request(app)
      .post(contract.request.path)
      .send({
        prompt: 'Test prompt',
        maxTokens: 100,
        temperature: 0.7
      });
    
    expect(response.status).toBe(contract.response.status);
    expect(response.body).toMatchObject(contract.response.body);
  });
});

// Hallucination detection in AI outputs
test('AI response accuracy', async () => {
  const response = await aiService.generate({
    prompt: 'What is the capital of France?'
  });
  
  // Check for known facts
  expect(response.content.toLowerCase()).toContain('paris');
  expect(response.content.toLowerCase()).not.toContain('london');
  expect(response.content.toLowerCase()).not.toContain('berlin');
  
  // Check for hallucinated URLs
  const urls = response.content.match(/https?:\/\/[^\s]+/g) || [];
  for (const url of urls) {
    const exists = await checkUrlExists(url);
    expect(exists).toBe(true);
  }
});
```

## CI/CD Testing

### Continuous Integration Tests
Optimize tests for CI/CD pipelines.

**Why:** Fast CI pipelines enable rapid iteration. Optimized test suites provide quick feedback while maintaining confidence in code quality.

```yaml
# GitHub Actions workflow
name: CI Pipeline

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]

jobs:
  # Fast feedback tier (< 2 minutes)
  quick-checks:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v3
      
      - name: Lint
        run: npm run lint
        
      - name: Type Check
        run: npm run type-check
        
      - name: Security Scan
        run: npm audit --audit-level=high
  
  # Unit tests in parallel
  unit-tests:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v3
      
      - name: Run Unit Tests (Shard ${{ matrix.shard }}/4)
        run: npm run test:unit -- --shard=${{ matrix.shard }}/4
        
      - name: Upload Coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/coverage-${{ matrix.shard }}.json
          flags: unit
  
  # Integration tests
  integration-tests:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    services:
      postgres:
        image: postgres:14
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Run Migrations
        run: npm run db:migrate:test
        
      - name: Run Integration Tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
          REDIS_URL: redis://localhost:6379
  
  # E2E tests (only on main)
  e2e-tests:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v3
      
      - name: Build Application
        run: npm run build
        
      - name: Run E2E Tests
        run: npm run test:e2e
        
      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-results
          path: test-results/
```

### Test Optimization
Speed up test execution.

**Why:** Fast tests encourage frequent running, catch bugs earlier, and improve developer productivity. Slow tests lead to skipped runs and delayed feedback.

```typescript
// Parallel test execution
module.exports = {
  maxWorkers: '50%',  // Use 50% of available CPU cores
  testTimeout: 10000,
  
  // Run slowest tests first
  testSequencer: './test-sequencer.js'
};

// Custom test sequencer
class CustomSequencer {
  sort(tests: Test[]): Test[] {
    const testDurations = this.getHistoricDurations();
    
    return tests.sort((a, b) => {
      const durationA = testDurations[a.path] || 0;
      const durationB = testDurations[b.path] || 0;
      return durationB - durationA;  // Slowest first
    });
  }
}

// Test splitting for CI
const splitTests = (tests: string[], totalShards: number, shardIndex: number): string[] => {
  const testsPerShard = Math.ceil(tests.length / totalShards);
  const start = (shardIndex - 1) * testsPerShard;
  const end = start + testsPerShard;
  
  return tests.slice(start, end);
};

// Selective test running based on changes
async function getTestsForChanges(changedFiles: string[]): Promise<string[]> {
  const tests = new Set<string>();
  
  for (const file of changedFiles) {
    // Direct test files
    if (file.endsWith('.test.ts')) {
      tests.add(file);
    }
    
    // Find tests that import this file
    const dependentTests = await findDependentTests(file);
    dependentTests.forEach(test => tests.add(test));
  }
  
  return Array.from(tests);
}

// Test caching
class TestCache {
  async getCachedResult(testId: string, inputHash: string): Promise<TestResult | null> {
    const cached = await redis.get(`test:${testId}:${inputHash}`);
    return cached ? JSON.parse(cached) : null;
  }
  
  async cacheResult(testId: string, inputHash: string, result: TestResult): Promise<void> {
    await redis.setex(
      `test:${testId}:${inputHash}`,
      3600,  // 1 hour TTL
      JSON.stringify(result)
    );
  }
}
```

