Teststrategie mit Claude Code: Vitest, Testing Library, Playwright und CI
Praktische Teststrategie mit Claude Code für Unit-, Integrations-, E2E- und CI-Tests.
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
| Ebene | Richtwert | Schützt | Tools |
|---|---|---|---|
| Unit | 60-70% | pure Logik, Validierung, Rechte | Vitest |
| Integration | 20-30% | Komponenten, API, DB-Grenzen | Vitest + Testing Library |
| E2E | 5-10% | Signup, Kauf, Umsatzpfade | Playwright |
| CI-Gate | jeder PR | lint, Typen, Tests, Reports | GitHub 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.
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.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude-Code-Permission-Receipt: Scope, Beweis und Rollback festhalten
Permission-Receipt für Claude Code: erlaubte Aktionen, Freigabegrenzen, Prüfbefehle, Rollback und Umsatz-CTA-Prüfung.
Sicheres Agent Harness fur Claude Code und Codex: Rechte, Prufung und Rollback
Ein praktisches Agent Harness fur Claude Code und Codex mit Policy, Plan, Verifikation und Recovery.
Claude Code Subagents: Praxisleitfaden für sichere Agent-Delegation
Claude Code Subagents praktisch nutzen: Artikel- und Codearbeit sicher aufteilen, Prompts einsetzen, Fehler vermeiden.