Test Pyramid Examples - Guide de Stratégie de Test

Exemples complets d'implémentation de pyramide de tests incluant tests unitaires, tests d'intégration, tests E2E, organisation de tests et patterns de test stratégiques pour assurance qualité logicielle équilibrée

📝 Fondements et Principes de la Pyramide de Tests markdown

🟢 simple ⭐⭐

Implémentation complète de pyramide de tests avec niveaux de tests équilibrés, organisation appropriée et directives de placement stratégique de tests

⏱️ 60 min 🏷️ test pyramid, strategy, foundation, principles
Prerequisites: Testing fundamentals, JavaScript/TypeScript, Test frameworks
# Test Pyramid Foundation and Principles

## Overview
The Test Pyramid is a testing strategy that emphasizes a balanced approach to software testing with the majority of tests being fast, isolated unit tests at the base, fewer integration tests in the middle, and even fewer end-to-end tests at the top.

## Test Pyramid Structure

### Level 1: Unit Tests (70%)
- **Purpose**: Test individual components in isolation
- **Characteristics**: Fast, cheap, numerous, reliable
- **Examples**: Function validation, class behavior, component logic
- **Tools**: Jest, Mocha, JUnit, Pytest

### Level 2: Integration Tests (20%)
- **Purpose**: Test how components work together
- **Characteristics**: Medium speed, moderate cost, fewer than unit tests
- **Examples**: Database interactions, API integrations, service communication
- **Tools**: TestContainers, Supertest, Spring Boot Test

### Level 3: End-to-End Tests (10%)
- **Purpose**: Test complete user workflows
- **Characteristics**: Slow, expensive, few, comprehensive
- **Examples**: User journeys, critical paths, cross-system workflows
- **Tools**: Playwright, Cypress, Selenium

## Implementation Strategy

### 1. Project Structure
```
project/
├── src/
│   ├── components/          # UI components
│   ├── services/           # Business logic
│   ├── utils/              # Utilities
│   └── api/                # API layer
├── tests/
│   ├── unit/               # Unit tests
│   │   ├── components/
│   │   ├── services/
│   │   └── utils/
│   ├── integration/        # Integration tests
│   │   ├── api/
│   │   ├── database/
│   │   └── services/
│   ├── e2e/                # End-to-end tests
│   │   ├── user-journeys/
│   │   └── critical-paths/
│   └── fixtures/           # Test data and utilities
└── test-configs/           # Test configuration files
```

## Best Practices

### 1. Test Organization
- Group related tests in describe blocks
- Use clear, descriptive test names
- Follow AAA pattern (Arrange, Act, Assert)
- Keep tests independent and isolated

### 2. Test Data
- Use factories or fixtures for test data
- Avoid hardcoded test data
- Clean up test data after each test
- Use realistic but simple data

### 3. Test Maintenance
- Regular review and refactoring
- Remove redundant tests
- Update tests when requirements change
- Monitor test execution time

### 4. Coverage Targets
- Unit tests: 80-90% coverage
- Integration tests: Critical paths coverage
- E2E tests: Core user journeys coverage

This foundation provides a solid base for implementing a comprehensive test pyramid strategy that ensures quality while maintaining development velocity.

💻 Patterns d'Implémentation de Pyramide de Tests javascript

🔴 complex ⭐⭐⭐⭐

Patterns d'implémentation avancés incluant automatisation de tests, intégration CI/CD, gestion de données de test et stratégies de test au niveau entreprise

⏱️ 90 min 🏷️ test pyramid, implementation, automation, enterprise
Prerequisites: Advanced testing, CI/CD, JavaScript/TypeScript, DevOps
// Test Pyramid Implementation Patterns

// 1. test-setup/base-test-setup.js - Shared Test Infrastructure
const { beforeAll, afterAll, beforeEach, afterEach } = require('@jest/globals');
const { setupDatabase, cleanupDatabase } = require('./database-setup');
const { startTestServer, stopTestServer } = require('./test-server');
const TestLogger = require('./test-logger');

class BaseTestSetup {
  constructor(options = {}) {
    this.options = {
      useDatabase: options.useDatabase !== false,
      useTestServer: options.useTestServer !== false,
      logLevel: options.logLevel || 'info',
      timeout: options.timeout || 10000,
      ...options
    };

    this.testLogger = new TestLogger(this.options.logLevel);
  }

  async setupGlobal() {
    if (this.options.useDatabase) {
      await setupDatabase();
      this.testLogger.info('Database setup complete');
    }

    if (this.options.useTestServer) {
      this.server = await startTestServer();
      this.testLogger.info('Test server started');
    }
  }

  async cleanupGlobal() {
    if (this.server) {
      await stopTestServer(this.server);
      this.testLogger.info('Test server stopped');
    }

    if (this.options.useDatabase) {
      await cleanupDatabase();
      this.testLogger.info('Database cleanup complete');
    }
  }

  async setupEach() {
    if (this.options.useDatabase) {
      await this.resetDatabase();
    }

    this.testContext = {
      startTime: Date.now(),
      testId: Math.random().toString(36).substr(2, 9),
      logger: this.testLogger
    };
  }

  async cleanupEach(testResult) {
    if (this.testContext) {
      const duration = Date.now() - this.testContext.startTime;
      this.testLogger.info(`Test completed in ${duration}ms`, {
        testId: this.testContext.testId,
        result: testResult
      });
    }
  }

  async resetDatabase() {
    // Reset database to clean state
    await cleanupDatabase();
    await setupDatabase();
  }

  getTestContext() {
    return this.testContext;
  }
}

// 2. test-helpers/api-test-helper.js - API Testing Utilities
class ApiTestHelper {
  constructor(baseUrl = 'http://localhost:3000') {
    this.baseUrl = baseUrl;
    this.defaultHeaders = {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    };
  }

  async request(method, endpoint, data = null, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const config = {
      method,
      headers: { ...this.defaultHeaders, ...options.headers },
      ...options
    };

    if (data && method !== 'GET') {
      config.body = JSON.stringify(data);
    }

    const response = await fetch(url, config);

    return {
      status: response.status,
      headers: response.headers,
      data: await response.json(),
      ok: response.ok
    };
  }

  // Assertion helpers
  assertSuccess(response, expectedStatus = 200) {
    if (response.status !== expectedStatus) {
      throw new Error(`Expected status ${expectedStatus}, got ${response.status}`);
    }
  }

  assertContains(response, expectedData) {
    expect(response.data).toMatchObject(expectedData);
  }
}

// 3. test-helpers/database-test-helper.js - Database Testing Utilities
class DatabaseTestHelper {
  constructor(connection) {
    this.connection = connection;
    this.fixtures = {};
  }

  async loadFixtures(fixtureName, data) {
    await this.connection.query(
      `INSERT INTO ${fixtureName} (${Object.keys(data[0]).join(', ')}) VALUES ?`,
      [data.map(item => Object.values(item))]
    );

    this.fixtures[fixtureName] = data;
  }

  async clearTable(tableName) {
    await this.connection.query(`DELETE FROM ${tableName}`);
    delete this.fixtures[tableName];
  }

  async resetDatabase() {
    const tables = Object.keys(this.fixtures);
    for (const table of tables) {
      await this.clearTable(table);
    }
  }

  async countRecords(tableName) {
    const [result] = await this.connection.query(`SELECT COUNT(*) as count FROM ${tableName}`);
    return result[0].count;
  }

  async findRecord(tableName, criteria) {
    const whereClause = Object.keys(criteria)
      .map(key => `${key} = ?`)
      .join(' AND ');

    const values = Object.values(criteria);

    const [result] = await this.connection.query(
      `SELECT * FROM ${tableName} WHERE ${whereClause}`,
      values
    );

    return result[0] || null;
  }
}

module.exports = {
  BaseTestSetup,
  ApiTestHelper,
  DatabaseTestHelper
};