Advanced (Mis à jour: 02/06/2026)

Stratégie de test avec Claude Code : Vitest, Testing Library, Playwright et CI

Concevoir des tests unitaires, intégration, E2E et CI avec Claude Code, sans tests fragiles.

Stratégie de test avec Claude Code : Vitest, Testing Library, Playwright et CI

Une stratégie de test ne consiste pas à écrire plus de tests. Elle consiste à décider ce qui doit être protégé par des tests unitaires rapides, ce qui mérite des tests d’intégration, et quels parcours métier doivent être couverts par des tests E2E.

Si vous demandez simplement à Claude Code « ajoute des tests », il peut produire des assertions superficielles, des sélecteurs CSS fragiles ou des tests qui copient l’implémentation actuelle. Une meilleure demande précise la couche de test, le comportement attendu, la commande à exécuter et les limites de modification.

Cette version a été vérifiée avec les documentations officielles : Claude Code common workflows, Vitest coverage, Testing Library queries, ainsi que Playwright pour les locators, les assertions et la CI.

Partir de la pyramide

flowchart TB
  E2E["E2E : inscription, paiement, CTA revenus"]
  INT["Intégration : API, DB, formulaires, composants"]
  UNIT["Unitaire : calcul, validation, permissions"]
  E2E --> INT --> UNIT
CoucheRepèreCe qu’elle protègeOutils
Unitaire60-70%logique pure, validation, permissionsVitest
Intégration20-30%composants, API, limites DBVitest + Testing Library
E2E5-10%inscription, achat, chemins de revenuPlaywright
CIchaque PRlint, types, tests, rapportsGitHub Actions

Le calcul de prix va dans les tests unitaires. Un bouton d’achat va dans les tests d’intégration. Le passage d’un CTA d’article vers la page produits est un bon test E2E.

Configuration minimale

npm i -D vitest @vitest/coverage-v8 jsdom \
  @testing-library/react @testing-library/jest-dom @testing-library/user-event \
  @playwright/test
npx playwright install --with-deps
{
  "scripts": {
    "test": "vitest --run",
    "test:watch": "vitest",
    "test:coverage": "vitest --run --coverage",
    "test:e2e": "playwright test"
  }
}
// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    environment: "jsdom",
    setupFiles: ["./test/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 75,
        statements: 80,
      },
    },
  },
});
// test/setup.ts
import "@testing-library/jest-dom/vitest";

Vitest documente les providers v8 et istanbul. Pour Node et Chromium, v8 reste le choix le plus simple dans la plupart des projets.

Exemple 1 : tester le prix en unitaire

// src/lib/pricing.ts
export type PriceInput = {
  unitPrice: number;
  quantity: number;
  discountRate?: number;
  taxRate?: number;
};

export function calculateTotal({
  unitPrice,
  quantity,
  discountRate = 0,
  taxRate = 0.1,
}: PriceInput): number {
  if (!Number.isInteger(quantity) || quantity < 0) {
    throw new Error("quantity must be a non-negative integer");
  }
  if (unitPrice < 0) throw new Error("unitPrice must be non-negative");
  if (discountRate < 0 || discountRate > 1) {
    throw new Error("discountRate must be between 0 and 1");
  }
  const discounted = unitPrice * quantity * (1 - discountRate);
  return Math.round(discounted * (1 + taxRate));
}
// src/lib/pricing.test.ts
import { describe, expect, it } from "vitest";
import { calculateTotal } from "./pricing";

describe("calculateTotal", () => {
  it("calculates a tax-included total", () => {
    expect(calculateTotal({ unitPrice: 1000, quantity: 2 })).toBe(2200);
  });

  it("applies discount before tax", () => {
    expect(
      calculateTotal({ unitPrice: 1000, quantity: 2, discountRate: 0.2 })
    ).toBe(1760);
  });

  it("allows zero quantity", () => {
    expect(calculateTotal({ unitPrice: 1000, quantity: 0 })).toBe(0);
  });

  it("rejects invalid inputs", () => {
    expect(() => calculateTotal({ unitPrice: 1000, quantity: -1 })).toThrow(
      "quantity must be a non-negative integer"
    );
  });
});

Le piège classique est de viser 80% de couverture avec seulement des cas heureux. Pour un prix, les vrais risques sont la quantité zéro, le rabais total, les taux invalides et l’arrondi.

Exemple 2 : tester un CTA avec Testing Library

Testing Library recommande les requêtes proches de l’expérience utilisateur. getByRole et getByLabelText rendent les tests plus robustes qu’un sélecteur CSS.

// src/components/CheckoutButton.tsx
import { useState } from "react";

type Props = {
  stock: number;
  onCheckout: () => Promise<void>;
};

export function CheckoutButton({ stock, onCheckout }: Props) {
  const [submitting, setSubmitting] = useState(false);
  const soldOut = stock <= 0;

  async function handleClick() {
    setSubmitting(true);
    try {
      await onCheckout();
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <button
      type="button"
      disabled={soldOut || submitting}
      aria-busy={submitting}
      onClick={handleClick}
    >
      {soldOut ? "Sold out" : submitting ? "Processing..." : "Buy now"}
    </button>
  );
}
// src/components/CheckoutButton.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { CheckoutButton } from "./CheckoutButton";

describe("CheckoutButton", () => {
  it("calls checkout when in stock", async () => {
    const user = userEvent.setup();
    const onCheckout = vi.fn().mockResolvedValue(undefined);

    render(<CheckoutButton stock={3} onCheckout={onCheckout} />);
    await user.click(screen.getByRole("button", { name: "Buy now" }));

    expect(onCheckout).toHaveBeenCalledTimes(1);
  });

  it("prevents checkout when sold out", async () => {
    const user = userEvent.setup();
    const onCheckout = vi.fn().mockResolvedValue(undefined);

    render(<CheckoutButton stock={0} onCheckout={onCheckout} />);
    const button = screen.getByRole("button", { name: "Sold out" });

    expect(button).toBeDisabled();
    await user.click(button);
    expect(onCheckout).not.toHaveBeenCalled();
  });
});

Un CTA lié aux revenus ne doit pas dépendre de .primary-button ni d’un snapshot seul. Il faut vérifier le rôle, le libellé et le comportement.

Exemple 3 : E2E Playwright ciblé

// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";

const baseURL =
  process.env.PLAYWRIGHT_TEST_BASE_URL ?? "http://127.0.0.1:5173";

export default defineConfig({
  testDir: "./e2e",
  retries: process.env.CI ? 2 : 0,
  use: { baseURL, trace: "on-first-retry" },
  projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
  webServer: process.env.PLAYWRIGHT_TEST_BASE_URL
    ? undefined
    : {
        command: "npm run dev -- --host 127.0.0.1",
        url: baseURL,
        reuseExistingServer: !process.env.CI,
      },
});
// e2e/article-cta.spec.ts
import { expect, test } from "@playwright/test";

test("reader can move from article CTA to products", async ({ page }) => {
  await page.goto("/fr/blog/claude-code-testing-strategies");

  await page.getByRole("link", { name: /products|templates|produits/i }).click();

  await expect(page).toHaveURL(/\/products\/?$/);
  await expect(page.getByRole("heading", { name: /products|produits/i })).toBeVisible();
});

Les assertions web-first de Playwright réessaient automatiquement. Préférez await expect(locator).toBeVisible() aux attentes fixes, et évitez les chemins CSS profonds ou nth().

Exemple 4 : ajouter la CI

# .github/workflows/test.yml
name: Test

on:
  pull_request:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run test:coverage
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
        env:
          CI: "true"
      - uses: actions/upload-artifact@v5
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Pour Claude Code GitHub Actions, la documentation actuelle présente anthropics/claude-code-action@v1 comme action GA. Ne reprenez pas d’anciens exemples @beta sans vérification.

Modèles de demande pour Claude Code

Ajoute des tests unitaires Vitest pour src/lib/pricing.ts.
Couvre les cas valides, les valeurs limites et les entrées invalides.
Ne change pas l'implémentation sauf si tu listes d'abord le bug suspect.
Exécute npm run test -- pricing et résume le résultat.
Ajoute des tests Testing Library pour src/components/CheckoutButton.tsx.
Utilise getByRole et userEvent.setup().
N'utilise pas de sélecteurs CSS, de snapshot comme assertion principale ni de détails internes.
Couvre les états en stock, épuisé et en soumission.
Ajoute un seul test Playwright E2E pour le flux CTA article vers produits.
Évite waitForTimeout, les sélecteurs CSS profonds et nth().
Utilise des assertions web-first et garde le test centré sur le revenu.
Analyse le dernier échec CI.
Classe-le en lint, typecheck, unit, e2e ou environnement.
Corrige la cause racine avec le plus petit diff.
Ne saute pas de tests et n'augmente pas seulement les timeouts.

Pièges fréquents

Ne mockez pas tout. Les services de paiement et d’email peuvent être simulés, mais les prix, permissions et écritures critiques doivent être testés au bon niveau.

Ne figez pas tous les détails visuels avec E2E. Les tests E2E doivent protéger l’inscription, l’achat, le contact et les clics produits.

Ne prenez pas la couverture comme une preuve de qualité. 80% peut encore manquer la branche qui supprime, facture ou publie.

Ne donnez pas seulement le cas heureux à Claude Code. Ajoutez les interdits : pas de sélecteurs fragiles, pas de skip, pas de snapshot-only, pas de couplage aux détails internes.

CTA

Les tests protègent aussi les chemins de revenu. Commencez avec la Claude Code cheatsheet, transformez les règles répétées en products and templates, et utilisez Claude Code training si l’équipe doit aligner CI, review et responsabilité des tests. Pour aller plus loin, lisez TDD with Claude Code, CI/CD setup et debugging techniques.

Résultat de l’essai pratique

Masa a appliqué ce modèle aux CTA d’articles et aux chemins vers les produits de ClaudeCodeLab. Le meilleur résultat n’a pas été d’ajouter beaucoup d’E2E, mais de garder la logique de prix en tests unitaires, le comportement CTA en Testing Library, et un seul test Playwright pour le trajet article-produits. Demander à Claude Code de lister d’abord les modes d’échec a réduit les tests ignorés et les sélecteurs CSS fragiles. Le risque restant concerne les paiements externes et scripts publicitaires, qui exigent encore un rapport CI et une courte vérification manuelle avant publication.

#Claude Code #testing #test strategy #Vitest #Playwright
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.