Advanced (Aktualisiert: 2.6.2026)

Teststrategie mit Claude Code: Vitest, Testing Library, Playwright und CI

Praktische Teststrategie mit Claude Code für Unit-, Integrations-, E2E- und CI-Tests.

Teststrategie mit Claude Code: Vitest, Testing Library, Playwright und CI

Eine Teststrategie bedeutet nicht, einfach mehr Tests zu schreiben. Sie legt fest, welches Verhalten schnelle Unit-Tests absichern, welche Grenzen Integrationstests brauchen und welche geschäftskritischen Abläufe in E2E-Tests gehören.

Wenn du Claude Code nur bittest, “Tests hinzuzufügen”, entstehen leicht oberflächliche Assertions, fragile CSS-Selektoren oder Tests, die nur die aktuelle Implementierung nachzeichnen. Eine bessere Anfrage nennt Testebene, Zielverhalten, auszuführende Befehle und klare Änderungsgrenzen.

Diese Anleitung wurde mit den offiziellen Dokumentationen abgeglichen: Claude Code common workflows, Vitest coverage, Testing Library queries sowie Playwright locators, assertions und CI.

Mit der Pyramide starten

flowchart TB
  E2E["E2E: Signup, Checkout, Revenue-CTA"]
  INT["Integration: API, DB, Formulare, Komponenten"]
  UNIT["Unit: Berechnung, Validierung, Rechte"]
  E2E --> INT --> UNIT
EbeneRichtwertSchütztTools
Unit60-70%pure Logik, Validierung, RechteVitest
Integration20-30%Komponenten, API, DB-GrenzenVitest + Testing Library
E2E5-10%Signup, Kauf, UmsatzpfadePlaywright
CI-Gatejeder PRlint, Typen, Tests, ReportsGitHub Actions

Preislogik gehört in Unit-Tests. Ein Checkout-Button gehört in Integrationstests. Der Weg von einem Artikel-CTA zur Produktseite ist ein sinnvoller E2E-Test.

Minimales Setup

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 dokumentiert v8 und istanbul als Coverage Provider. Für Node- und Chromium-Projekte ist v8 meistens die einfachste Wahl.

Beispiel 1: Preislogik als Unit-Test

// 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"
    );
  });
});

Der typische Fehler ist, 80% Coverage nur mit Happy Paths zu erreichen. Bei Preisen sind Menge null, Vollrabatt, ungültige Raten und Rundung wichtiger.

Beispiel 2: CTA mit Testing Library testen

Testing Library empfiehlt Abfragen, die der Nutzung durch Menschen ähneln. getByRole und getByLabelText sind robuster als CSS-Klassen.

// 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();
  });
});

Ein Revenue-CTA sollte nicht über .primary-button oder reine Snapshots abgesichert werden. Entscheidend sind Rolle, Text und Verhalten.

Beispiel 3: Playwright E2E klein halten

// 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("/de/blog/claude-code-testing-strategies");

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

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

Playwrights web-first Assertions warten automatisch erneut. Nutze await expect(locator).toBeVisible() statt fester Wartezeiten. Vermeide tiefe CSS-Pfade und nth().

Beispiel 4: CI-Gate

# .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

Wenn du Claude Code GitHub Actions nutzt, orientiere dich an der aktuellen GA-Aktion anthropics/claude-code-action@v1. Alte @beta-Beispiele solltest du nicht ungeprüft übernehmen.

Prompt-Vorlagen für Claude Code

Füge Vitest-Unit-Tests für src/lib/pricing.ts hinzu.
Decke gültige Fälle, Grenzwerte und ungültige Eingaben ab.
Ändere die Implementierung nicht, außer du listest zuerst den vermuteten Bug.
Führe npm run test -- pricing aus und fasse das Ergebnis zusammen.
Füge Testing-Library-Tests für src/components/CheckoutButton.tsx hinzu.
Nutze getByRole und userEvent.setup().
Nutze keine CSS-Selektoren, Snapshots als Hauptassertion oder interne Details.
Decke Lagerbestand, ausverkauft und submitting ab.
Füge genau einen Playwright-E2E-Test für den CTA-Fluss vom Artikel zu Produkten hinzu.
Vermeide waitForTimeout, tiefe CSS-Selektoren und nth().
Nutze web-first Assertions und halte den Test auf den Revenue-Pfad fokussiert.
Untersuche den neuesten CI-Fehler.
Klassifiziere ihn als lint, typecheck, unit, e2e oder environment.
Behebe die Ursache mit dem kleinsten Diff.
Tests nicht skippen und nicht nur Timeouts erhöhen.

Häufige Fallen

Mocke nicht alles. Payment-Provider und E-Mail-Versand dürfen gemockt werden, aber Preise, Rechte und wichtige Persistenz müssen auf der passenden Ebene getestet werden.

Fixiere nicht jedes visuelle Detail mit E2E. E2E soll Anmeldung, Kauf, Kontakt und Produktklicks schützen.

Behandle Coverage nicht als Qualitätsbeweis. 80% können trotzdem genau die Lösch-, Zahlungs- oder Publishing-Branch übersehen.

Gib Claude Code nicht nur den Happy Path. Nenne auch Verbote: keine fragilen Selektoren, kein skip, keine Snapshot-only-Tests und keine Kopplung an Implementierungsdetails.

CTA

Tests schützen auch Umsatzpfade. Starte mit der kostenlosen Claude Code cheatsheet, mache wiederkehrende Review-Regeln zu products and templates, und nutze Claude Code training, wenn ein Team CI, Review-Regeln und Testverantwortung gemeinsam aufbauen muss. Passende Vertiefungen sind TDD with Claude Code, CI/CD setup und debugging techniques.

Praktisch ausprobiert

Masa hat dieses Muster auf ClaudeCodeLab-Artikel-CTAs und Produktpfade angewendet. Am meisten gebracht hat nicht mehr E2E, sondern Preislogik in Unit-Tests, CTA-Verhalten in Testing Library und ein einzelner Playwright-Test für den Weg Artikel zu Produkten. Wenn Claude Code zuerst Fehlermodi auflistete, wurden übersprungene Tests und fragile CSS-Selektoren seltener. Das Restrisiko liegt bei externen Zahlungen und Werbeskripten; dafür braucht es weiter CI-Nachweise und einen kurzen manuellen Release-Check.

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

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.