Aller au contenu

Tests d'intégration et E2E

Tester les interfaces entre composants, les API et les parcours utilisateur de bout en bout.


Tests d'intégration

Un test d'intégration vérifié que plusieurs composants fonctionnent correctement ensemble. La ou le test unitaire isole une fonction, le test d'intégration teste les frontieres.

graph LR
    subgraph "Test unitaire"
        A["Fonction A"]
    end
    subgraph "Test d'integration"
        B["Service"] --> C["Base de donnees"]
        B --> D["Cache"]
    end
    subgraph "Test E2E"
        E["Navigateur"] --> F["API"] --> G["DB"]
    end

Quoi tester en intégration

Frontiere Ce qu'on vérifié
Service → Base de données Les requêtes SQL fonctionnent, les migrations sont correctes
Service → Service Les contrats d'API sont respectes
Service → File de messages Les messages sont serialises/deserialises correctement
Service → Cache Les clés, les TTL, l'invalidation

Testcontainers — environnements éphémères

Au lieu de mocker la base de données, lancez-en une vraie dans un conteneur :

# Python avec testcontainers
from testcontainers.postgres import PostgresContainer

def test_user_repository():
    with PostgresContainer("postgres:16") as pg:
        engine = create_engine(pg.get_connection_url())
        repo = UserRepository(engine)

        repo.create(User(name="Alice", email="alice@example.com"))
        user = repo.find_by_email("alice@example.com")

        assert user.name == "Alice"
// Java avec Testcontainers
@Testcontainers
class UserRepositoryTest {
    @Container
    static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16");

    @Test
    void shouldCreateAndFindUser() {
        var repo = new UserRepository(pg.getJdbcUrl());
        repo.create(new User("Alice", "alice@example.com"));

        var user = repo.findByEmail("alice@example.com");
        assertEquals("Alice", user.getName());
    }
}

Pourquoi pas de mocks pour la DB ?

Un mock de base de données ne détecté pas les erreurs SQL, les problèmes de migration, ni les différences de comportement entre moteurs. Testcontainers lance un vrai PostgreSQL/MySQL/Redis en quelques secondes — la confiance est incomparable.


Tests de contrats

Les tests de contrats verifient que le contrat d'interface entre un consommateur et un fournisseur est respecte, sans que les deux soient déployés ensemble.

graph LR
    C["Consommateur<br/>Frontend"] -->|"contrat"| P["Fournisseur<br/>API Backend"]
    C -->|"genere"| CT["Contract Test<br/>(cote consommateur)"]
    CT -->|"verifie contre"| P

Consumer-Driven Contracts avec Pact

  1. Le consommateur définit ses attentes (ce qu'il appelle, ce qu'il attend)
  2. Pact généré un fichier de contrat (JSON)
  3. Le fournisseur exécuté le contrat contre son code
  4. Si le contrat passe, les deux sont compatibles
# Cote consommateur (Python)
from pact import Consumer, Provider

pact = Consumer('Frontend').has_pact_with(Provider('UserAPI'))

(pact
    .given('un utilisateur Alice existe')
    .upon_receiving('une requete GET /users/alice')
    .with_request('get', '/users/alice')
    .will_respond_with(200, body={
        'name': 'Alice',
        'email': 'alice@example.com'
    }))

with pact:
    result = get_user('alice')  # appelle le mock Pact
    assert result['name'] == 'Alice'

API Testing

Les tests d'API verifient le comportement de vos endpoints HTTP sans passer par l'interface utilisateur.

Avec le framework de test

# FastAPI + pytest
from fastapi.testclient import TestClient

def test_create_user(client: TestClient):
    response = client.post("/users", json={
        "name": "Alice",
        "email": "alice@example.com"
    })
    assert response.status_code == 201
    assert response.json()["name"] == "Alice"

def test_create_user_duplicate_email(client: TestClient):
    client.post("/users", json={"name": "Alice", "email": "alice@example.com"})
    response = client.post("/users", json={"name": "Bob", "email": "alice@example.com"})
    assert response.status_code == 409

Avec des outils dédiés

Outil Usage
Postman/Newman Collections de tests API avec assertions et variables
Bruno Alternative open source et Git-friendly a Postman
Hurl Fichiers texte HTTP executables en CI
REST Client Extension VSCode pour tester directement dans l'éditeur

Tests E2E

Les tests E2E simulent un utilisateur réel : navigateur, clics, saisie de texte, navigation entre pages.

Avec Playwright

# Python Playwright
from playwright.sync_api import Page

def test_login_flow(page: Page):
    page.goto("/login")
    page.fill("#email", "alice@example.com")
    page.fill("#password", "P@ssw0rd!")
    page.click("#submit")

    # Verification
    assert page.url == "/dashboard"
    assert page.text_content("h1") == "Bienvenue Alice"
// JavaScript Playwright
test('login flow', async ({ page }) => {
    await page.goto('/login');
    await page.fill('#email', 'alice@example.com');
    await page.fill('#password', 'P@ssw0rd!');
    await page.click('#submit');

    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toHaveText('Bienvenue Alice');
});

Bonnes pratiques E2E

Pratique Raison
Peu de tests E2E (parcours critiques) Lents et fragiles — reservez-les au essentiel
Données de test isolées Chaque test crée ses données et les nettoie
Selecteurs stables (data-testid) Les classes CSS changent, les testid non
Retries automatiques Les tests E2E sont inherement flaky
Screenshots sur échec Diagnostic instantané en CI

Gestion des données de test

Stratégie Description Adapté pour
Fixtures Données prédéfinies chargees avant le test Tests deterministes
Factories Generateurs d'objets avec valeurs par défaut Tests varies
Seed database Base pre-remplie pour les tests d'intégration Suites larges
Ephemeral containers Base vierge dans un conteneur par test Isolation maximale

Outils

Outil Type Lien
Playwright E2E multi-navigateur playwright.dev
Cypress E2E JavaScript cypress.io
Testcontainers Conteneurs éphémères testcontainers.com
Pact Contract testing pact.io
Postman/Newman API testing postman.com
Bruno API testing Git-friendly usebruno.com
Hurl HTTP testing CLI hurl.dev