Playwright
Claude Code
E2E Testing
TypeScript
Page Object Model
API Mocking
GitHub Actions
CI/CD
Visual Testing
End-to-End-Tests sind 2026 keine optionale Zugabe mehr — sie sind der Unterschied zwischen einer App, die man mit Vertrauen deployed, und einer, bei der jedes Release zur Zitterpartie wird. Playwright hat sich gegenüber Cypress, Selenium und Puppeteer als klarer Favorit durchgesetzt: Multi-Browser out of the box, native TypeScript-Unterstützung, Auto-Waiting und ein Tracing-Tool, das bei Fehlerdiagnosen seinesgleichen sucht.
Claude Code verändert dabei die Art, wie wir Tests schreiben. Statt stundenlang Locators zu tippen, POM-Klassen aufzusetzen und CI-Pipelines zu konfigurieren, beschreibt man dem Agenten was getestet werden soll — und bekommt produktionsreife TypeScript-Tests zurück. In diesem Artikel zeigen wir die komplette Playwright-Werkzeugkiste, ergänzt durch konkrete Prompts und Workflows mit Claude Code.
🌐
Multi-Browser
Chromium, Firefox, WebKit — alle drei parallel in CI
⚡
Auto-Wait
Playwright wartet automatisch auf Element-Bereitschaft
🔌
API Mocking
Network Interception direkt im Test — kein separates Mock-Server
📸
Visual Tests
Screenshot-Vergleiche mit konfigurierbarer Schwelle
🎬
Tracing
Vollständige Test-Aufzeichnung für Fehleranalyse
🤖
Claude Code
Test-Suiten automatisch generieren lassen
1Playwright Setup mit Claude Code
Der schnellste Einstieg in Playwright gelingt mit dem offiziellen Init-Befehl. Claude Code kann diesen Prozess vollständig automatisieren und gleichzeitig eine sinnvolle Konfiguration für das jeweilige Projekt generieren.
Installation und Initialisierung
Claude Code Prompt: "Richte Playwright für unser Next.js-Projekt ein. TypeScript, alle drei Browser, baseURL auf localhost:3000."
# Playwright im bestehenden Projekt initialisieren
npm init playwright@latest
# Oder in einem neuen Projekt
npm create playwright@latest my-app
cd my-app
# Browser-Binaries installieren
npx playwright install
# Mit System-Dependencies (für CI)
npx playwright install --with-deps chromium firefox webkit
Nach dem Init erstellt Playwright eine playwright.config.ts im Projektstamm. Claude Code generiert eine optimierte Konfiguration, die alle gängigen Anforderungen abdeckt:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Verzeichnis mit allen Test-Dateien
testDir: './tests/e2e',
// Maximale Laufzeit pro Test (30 Sekunden)
timeout: 30_000,
// Globaler Setup/Teardown
globalSetup: './tests/global-setup.ts',
// Parallele Ausführung aktivieren
fullyParallel: true,
// CI: kein retry-Limit für flaky Tests verschleiern
forbidOnly: !!process.env.CI,
// Retries nur in CI
retries: process.env.CI ? 2 : 0,
// Parallele Worker
workers: process.env.CI ? 1 : undefined,
// Reporter-Konfiguration
reporter: [
['html', { open: 'never' }],
['junit', { outputFile: 'results.xml' }],
process.env.CI ? ['github'] : ['list']
],
// Globale Test-Einstellungen
use: {
// Basis-URL — alle Locators werden relativ dazu
baseURL: process.env.BASE_URL || 'http://localhost:3000',
// Screenshot bei Fehlern
screenshot: 'only-on-failure',
// Video-Aufnahme bei Fehlern
video: 'retain-on-failure',
// Tracing: bei erstem Retry aktivieren
trace: 'on-first-retry',
// Viewport
viewport: { width: 1280, height: 720 },
},
// Browser-Projekte (Matrix)
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Mobile Testing
{
name: 'mobile-chrome',
use: { ...devices['Pixel 7'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 14'] },
},
],
// Lokaler Dev-Server automatisch starten
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
});
Claude Code Tipp
Prompt: "Analysiere unsere package.json und erstelle eine passende playwright.config.ts mit allen Browser-Projekten, korrektem baseURL und CI-optimierten Einstellungen." Claude Code liest die bestehende Konfiguration und passt sich automatisch an.
Ersten Test ausführen
# Alle Tests ausführen
npx playwright test
# Einzelne Datei
npx playwright test tests/e2e/login.spec.ts
# Nur Chromium
npx playwright test --project=chromium
# Mit UI-Modus (interaktiv)
npx playwright test --ui
# Debug-Modus (Schritt für Schritt)
npx playwright test --debug
# HTML-Report öffnen
npx playwright show-report
🛠 Playwright Codegen
Playwright hat einen eingebauten Code-Generator, der Benutzeraktionen aufzeichnet und direkt TypeScript-Tests generiert. Ideal als Ausgangspunkt, den Claude Code dann verfeinert:
# Browser öffnen und Aktionen aufzeichnen
npx playwright codegen http://localhost:3000
# Mit Authentication-State
npx playwright codegen --load-storage=auth.json http://localhost:3000
2Locators & Assertions
Playwright-Locators sind 2026 deutlich robuster als die alten XPath- oder CSS-Selektor-Ansätze. Sie warten automatisch auf Sichtbarkeit, Interaktionsfähigkeit und Stabilität — ohne zusätzliche waitFor-Aufrufe.
Moderne Locator-Strategien
getByRole
getByText
getByTestId
getByLabel
getByPlaceholder
import { test, expect } from '@playwright/test';
test.describe('Locator-Strategien', () => {
test('getByRole — semantische Locators (bevorzugt)', async ({ page }) => {
await page.goto('/');
// Buttons nach ARIA-Rolle und Name
await page.getByRole('button', { name: 'Anmelden' }).click();
// Navigation-Links
await page.getByRole('link', { name: 'Blog' }).click();
// Überschriften
await expect(page.getByRole('heading', { level: 1 }))
.toHaveText('Willkommen bei SpockyMagicAI');
// Checkboxen
await page.getByRole('checkbox', { name: 'AGB akzeptieren' }).check();
});
test('getByText — Textinhalt-Locators', async ({ page }) => {
await page.goto('/dashboard');
// Exakten Text finden
const welcomeMsg = page.getByText('Guten Morgen, Daniel!');
await expect(welcomeMsg).toBeVisible();
// Partieller Textvergleich
const statusBadge = page.getByText(/aktiv/i);
await expect(statusBadge).toBeVisible();
// Klick auf Text-Element
await page.getByText('Mehr anzeigen').click();
});
test('getByTestId — stabile Test-Attribute', async ({ page }) => {
await page.goto('/checkout');
// data-testid Attribute (stabil gegenüber UI-Änderungen)
const total = page.getByTestId('checkout-total');
await expect(total).toHaveText('€ 49,00');
await page.getByTestId('checkout-submit-btn').click();
// Warten auf Success-Meldung
await expect(page.getByTestId('order-confirmation'))
.toBeVisible({ timeout: 10_000 });
});
test('Chaining — verschachtelte Locators', async ({ page }) => {
await page.goto('/products');
// Innerhalb eines Containers suchen
const productCard = page.getByTestId('product-card').first();
const addToCart = productCard.getByRole('button', { name: 'In den Warenkorb' });
await addToCart.click();
// Filter-Locator: Element mit spezifischem Inhalt
const activeUser = page
.getByRole('listitem')
.filter({ hasText: 'Daniel Bratschke' });
await expect(activeUser).toBeVisible();
});
});
Assertions im Überblick
import { test, expect } from '@playwright/test';
test('vollständige Assertions-Demo', async ({ page }) => {
await page.goto('/login');
// ── Sichtbarkeit ──────────────────────────────────
await expect(page.getByRole('main')).toBeVisible();
await expect(page.getByTestId('spinner')).toBeHidden();
// ── Text-Vergleiche ───────────────────────────────
await expect(page.getByRole('heading', { level: 1 }))
.toHaveText('Anmelden');
await expect(page.getByTestId('user-count'))
.toContainText('Nutzer');
// ── Input-Werte ───────────────────────────────────
const emailInput = page.getByLabel('E-Mail-Adresse');
await emailInput.fill('test@example.com');
await expect(emailInput).toHaveValue('test@example.com');
// ── Attribute ─────────────────────────────────────
await expect(page.getByRole('button', { name: 'Anmelden' }))
.toHaveAttribute('type', 'submit');
await expect(page.getByRole('link', { name: 'Passwort vergessen?' }))
.toHaveAttribute('href', '/reset-password');
// ── CSS-Klassen ───────────────────────────────────
await expect(page.getByTestId('status-badge'))
.toHaveClass(/badge--active/);
// ── URL & Titel ───────────────────────────────────
await expect(page).toHaveURL('/login');
await expect(page).toHaveTitle(/SpockyMagicAI/);
// ── Soft Assertions (sammelt alle Fehler) ─────────
await expect.soft(page.getByTestId('logo')).toBeVisible();
await expect.soft(page.getByTestId('footer')).toBeVisible();
});
💡 Auto-Waiting erklärt
Playwright wartet bei jedem Locator automatisch auf: Element existiert im DOM, ist sichtbar, ist nicht von anderem Element verdeckt, ist nicht animiert, ist enabled. Kein manuelles waitForSelector mehr nötig.
3Page Object Model (POM)
Das Page Object Model trennt Test-Logik von UI-Interaktion. Statt Locators über hunderte Tests zu verteilen, kapselt jede POM-Klasse eine Seite oder Komponente. Claude Code generiert vollständige POM-Klassen aus einer kurzen Beschreibung:
Prompt: "Erstelle ein Page Object Model für unsere Login-Seite. Felder: E-Mail, Passwort. Aktionen: login(), loginWithGoogle(), resetPassword(). Navigiert nach /dashboard nach erfolgreichem Login."
POM
TypeScript
import { Page, Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
// Locators als readonly Properties — einmal definiert, überall genutzt
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly googleLoginButton: Locator;
readonly resetPasswordLink: Locator;
readonly errorMessage: Locator;
readonly successToast: Locator;
constructor(page: Page) {
this.page = page;
// Semantische Locators — robust gegen UI-Änderungen
this.emailInput = page.getByLabel('E-Mail-Adresse');
this.passwordInput = page.getByLabel('Passwort');
this.submitButton = page.getByRole('button', { name: 'Anmelden' });
this.googleLoginButton = page.getByRole('button', { name: /Google/i });
this.resetPasswordLink = page.getByRole('link', { name: 'Passwort vergessen?' });
this.errorMessage = page.getByRole('alert');
this.successToast = page.getByTestId('toast-success');
}
// Navigation
async navigate(): Promise<void> {
await this.page.goto('/login');
await expect(this.emailInput).toBeVisible();
}
// Aktionen: Login mit E-Mail + Passwort
async login(email: string, password: string): Promise<void> {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
// Erfolgreichen Login verifizieren
async loginAndVerify(email: string, password: string): Promise<void> {
await this.login(email, password);
await expect(this.page).toHaveURL('/dashboard');
}
// Fehlermeldung verifizieren
async expectError(message: string): Promise<void> {
await expect(this.errorMessage).toBeVisible();
await expect(this.errorMessage).toContainText(message);
}
// Password-Reset
async resetPassword(email: string): Promise<void> {
await this.resetPasswordLink.click();
await expect(this.page).toHaveURL('/reset-password');
await this.page.getByLabel('E-Mail-Adresse').fill(email);
await this.page.getByRole('button', { name: 'Link senden' }).click();
}
}
POM in Tests verwenden
import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
test.describe('Login-Flow', () => {
let loginPage: LoginPage;
let dashboardPage: DashboardPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
dashboardPage = new DashboardPage(page);
await loginPage.navigate();
});
test('erfolgreicher Login navigiert zu Dashboard', async () => {
await loginPage.loginAndVerify(
'test@example.com',
'SecurePass123!'
);
await expect(dashboardPage.welcomeHeading).toBeVisible();
});
test('falsches Passwort zeigt Fehlermeldung', async () => {
await loginPage.login('test@example.com', 'wrong');
await loginPage.expectError('Ungültige Anmeldedaten');
await expect(loginPage.page).toHaveURL('/login');
});
test('leeres Passwort-Feld zeigt Validierungsfehler', async () => {
await loginPage.emailInput.fill('test@example.com');
await loginPage.submitButton.click();
await loginPage.expectError('Bitte Passwort eingeben');
});
test('Password-Reset-Flow funktioniert', async () => {
await loginPage.resetPassword('test@example.com');
await expect(loginPage.successToast).toBeVisible();
await expect(loginPage.successToast)
.toContainText('E-Mail versendet');
});
});
POM-Vorteile auf einen Blick
- Wartbarkeit: Locator-Änderungen nur an einem Ort nötig
- Lesbarkeit: Tests lesen sich wie Spezifikationen
- Wiederverwendung: loginAndVerify() in 50 Tests nutzbar
- Testbarkeit der Tests: POM-Klassen isoliert unit-testbar
4API Mocking & Network Interception
Playwright kann HTTP-Requests direkt im Browser-Kontext abfangen — kein separater Mock-Server, keine komplexe Konfiguration. Claude Code generiert vollständige Mocking-Setups aus API-Beschreibungen:
API Mocking
HAR Recording
import { test, expect } from '@playwright/test';
test.describe('API Mocking', () => {
test('route.fulfill — Erfolgreiche API-Antwort simulieren', async ({ page }) => {
// API-Endpunkt abfangen und eigene Antwort liefern
await page.route('**/api/users/profile', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: 'user-123',
name: 'Daniel Bratschke',
email: 'daniel@example.com',
plan: 'pro',
trialEndsAt: '2026-06-01',
}),
});
});
await page.goto('/dashboard');
// Gemockter User-Name erscheint im Dashboard
await expect(page.getByText('Daniel Bratschke')).toBeVisible();
await expect(page.getByTestId('plan-badge')).toContainText('Pro');
});
test('route.abort — Netzwerkfehler simulieren', async ({ page }) => {
// Request gezielt fehlschlagen lassen
await page.route('**/api/analytics/**', route => route.abort());
// Analytics-Fehler soll App NICHT blocken
await page.goto('/dashboard');
await expect(page.getByRole('main')).toBeVisible();
// Error-State für Analytics-Widget prüfen
await expect(page.getByTestId('analytics-error')).toBeVisible();
});
test('API-Fehler (500) behandeln', async ({ page }) => {
await page.route('**/api/products', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({
error: 'Internal Server Error',
message: 'Datenbankverbindung fehlgeschlagen',
}),
});
});
await page.goto('/products');
// Error-Boundary oder Fallback-UI prüfen
await expect(page.getByTestId('error-message'))
.toContainText('Etwas ist schiefgelaufen');
await expect(page.getByRole('button', { name: 'Erneut versuchen' }))
.toBeVisible();
});
test('Request modifizieren und weiterleiten', async ({ page }) => {
// Original-Request abfangen, Header hinzufügen, weiterleiten
await page.route('**/api/**', async route => {
const request = route.request();
await route.continue({
headers: {
...request.headers(),
'X-Test-Mode': 'playwright',
'X-Test-User': 'e2e-test-user',
},
});
});
await page.goto('/dashboard');
});
test('HAR-Recording für realistische Mocks', async ({ page }) => {
// Aufgezeichnete HAR-Datei als Mock verwenden
await page.routeFromHAR('./tests/fixtures/api-responses.har', {
url: '**/api/**',
update: false, // true = HAR neu aufnehmen
});
await page.goto('/dashboard');
await expect(page.getByTestId('revenue-chart')).toBeVisible();
});
});
⚠ HAR-Datei aktuell halten
HAR-Dateien veralten schnell wenn sich API-Responses ändern. Führe regelmäßig npx playwright test --update-snapshots mit update: true aus, um die HAR-Dateien frisch zu halten.
Request-Monitoring und Logging
import { test, expect } from '@playwright/test';
test('API-Aufrufe überwachen und verifizieren', async ({ page }) => {
const apiRequests: string[] = [];
// Alle API-Requests sammeln
page.on('request', request => {
if (request.url().includes('/api/')) {
apiRequests.push(request.url());
}
});
// Auf spezifischen Request warten
const profileRequest = page.waitForRequest('**/api/users/profile');
await page.goto('/dashboard');
const req = await profileRequest;
// Request-Methode und Headers prüfen
expect(req.method()).toBe('GET');
expect(req.headers()['authorization']).toMatch(/Bearer .+/);
// Response abwarten und prüfen
const response = await page.waitForResponse('**/api/users/profile');
expect(response.status()).toBe(200);
const data = await response.json();
expect(data).toHaveProperty('id');
});
5Visual Comparisons & Tracing
Visual Testing erkennt ungewollte UI-Regressions automatisch. Playwright vergleicht Screenshots pixelgenau mit einem Baseline-Snapshot. Claude Code erstellt vollständige Visual-Test-Suiten mit konfigurierbaren Schwellenwerten:
Visual
Tracing
import { test, expect } from '@playwright/test';
test.describe('Visual Regression Tests', () => {
test('Homepage sieht korrekt aus', async ({ page }) => {
await page.goto('/');
// Warten bis Animationen und Fonts geladen sind
await page.waitForLoadState('networkidle');
// Ganzseitiger Screenshot-Vergleich
await expect(page).toHaveScreenshot('homepage.png', {
// 0.2% Pixelabweichung erlaubt (Anti-Aliasing etc.)
threshold: 0.2,
// Max. 100 fehlerhafte Pixel
maxDiffPixels: 100,
});
});
test('Dashboard-Header korrekt', async ({ page }) => {
await page.goto('/dashboard');
// Nur bestimmten Bereich screenshotten
const header = page.getByRole('banner');
await expect(header).toHaveScreenshot('dashboard-header.png', {
threshold: 0.1,
});
});
test('Dark Mode Theme korrekt', async ({ page }) => {
// Dark Mode via CSS Media Query emulieren
await page.emulateMedia({ colorScheme: 'dark' });
await page.goto('/pricing');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('pricing-dark.png', {
threshold: 0.3,
});
});
test('Mobile Viewport', async ({ page }) => {
// Mobile Viewport setzen
await page.setViewportSize({ width: 390, height: 844 });
await page.goto('/pricing');
await expect(page).toHaveScreenshot('pricing-mobile.png', {
threshold: 0.2,
});
});
test('Dynamische Elemente maskieren', async ({ page }) => {
await page.goto('/dashboard');
// Zeitstempel und Zufallsdaten maskieren (werden nicht verglichen)
await expect(page).toHaveScreenshot('dashboard-masked.png', {
mask: [
page.getByTestId('timestamp'),
page.getByTestId('random-quote'),
page.getByTestId('live-counter'),
],
threshold: 0.1,
});
});
});
Tracing für Fehlerdiagnose
Playwright Traces sind die mächtigste Debugging-Funktion: Sie zeichnen Screenshots, DOM-Snapshots, Netzwerk-Requests und Konsolen-Logs für jeden einzelnen Schritt auf. Claude Code kann Traces automatisch auswerten:
// In playwright.config.ts → use-Block
use: {
// 'on-first-retry' = Trace nur bei Fehler aufzeichnen (CI-empfohlen)
// 'on' = immer aufzeichnen (Development)
// 'retain-on-failure' = behalten wenn Test fehlschlägt
trace: 'on-first-retry',
// Slow-Mo für manuelle Analyse
launchOptions: {
slowMo: process.env.SLOW_MO ? 500 : 0,
},
},
# Trace lokal öffnen
npx playwright show-trace test-results/trace.zip
# Trace online (keine Installation nötig)
# Datei auf trace.playwright.dev hochladen
# Test mit Tracing ausführen und Trace direkt öffnen
npx playwright test --trace on && npx playwright show-trace
# Screenshots-Vergleich-Update (nach bewussten UI-Änderungen)
npx playwright test --update-snapshots
Trace Viewer zeigt alles:
- Timeline: Jeder Schritt mit Zeitstempel und Dauer
- DOM-Snapshots: Browser-State vor/nach jeder Aktion
- Network: Alle Requests mit Headers, Body, Response
- Console: Alle Log-Ausgaben und Fehler
- Source: Welche Zeile im Test-Code gerade lief
6CI/CD Integration mit GitHub Actions
Eine vollständige Playwright-Pipeline in GitHub Actions: Browser-Matrix (Chromium, Firefox, WebKit), Test-Sharding für Parallelisierung, HTML-Report als Artefakt. Claude Code generiert diese Konfiguration auf Knopfdruck:
CI/CD
GitHub Actions
Matrix
name: Playwright E2E Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
# Browser-Matrix: Tests laufen parallel für alle Browser
strategy:
fail-fast: false
matrix:
browser: [chromium, firefox, webkit]
shard: [1/4, 2/4, 3/4, 4/4]
steps:
- uses: actions/checkout@v4
- name: Node.js Setup
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Dependencies installieren
run: npm ci
# Browser-Binaries mit System-Dependencies installieren
- name: Playwright Browser installieren
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Tests ausführen
run: |
npx playwright test \
--project=${{ matrix.browser }} \
--shard=${{ matrix.shard }}
env:
CI: true
BASE_URL: http://localhost:3000
# Blob-Report für spätere Zusammenführung speichern
- name: Blob Report hochladen
if: always()
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.browser }}-${{ strategy.job-index }}
path: blob-report/
retention-days: 30
# Merge-Job: Alle Shard-Reports zusammenführen
merge-reports:
needs: [test]
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- name: Blob Reports herunterladen
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Reports zusammenführen
run: npx playwright merge-reports --reporter html ./all-blob-reports
# HTML-Report als Artefakt 30 Tage aufbewahren
- name: HTML Report hochladen
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
Deployment-Gate: Kein Merge bei roten Tests
jobs:
deploy:
needs: [test] # Deployment nur wenn alle Tests grün
if: success()
runs-on: ubuntu-latest
steps:
- name: Auf Production deployen
run: |
# Nur ausgeführt wenn Playwright-Tests grün sind
npm run deploy:production
Sharding für schnellere CI
Mit 3 Browsern × 4 Shards laufen 12 Jobs parallel. Eine Test-Suite mit 200 Tests wird so von ~20 Minuten auf ~2 Minuten reduziert. Claude Code berechnet die optimale Shard-Anzahl basierend auf der Test-Anzahl.
Lokale Pre-Commit Hooks
#!/bin/sh
# Nur betroffene Tests vor Push ausführen (schnell)
# Geänderte Dateien ermitteln
CHANGED=$(git diff --name-only HEAD~1)
# Playwright nur wenn E2E-Tests oder Source geändert
if echo "$CHANGED" | grep -qE "(tests/e2e|src/)"; then
echo "E2E-Tests vor Push ausführen..."
npx playwright test --project=chromium
fi
🟢
Grüne CI
Deployment-Gate blockiert automatisch bei fehlgeschlagenen Tests
⚡
Sharding
200 Tests in 2 Minuten statt 20 durch parallele Ausführung
📊
HTML Reports
Übersichtliche Test-Reports direkt in GitHub Actions
🔁
Retries
2 Retries in CI fangen flaky Tests ab ohne Builds zu blocken
★Playwright vs. Alternativen 2026
| Feature |
Playwright |
Cypress |
Selenium |
| Multi-Browser out of the box |
✓ Chromium, Firefox, WebKit |
Eingeschränkt |
✓ |
| TypeScript nativ |
✓ |
✓ |
Extern |
| Auto-Waiting |
✓ (built-in) |
✓ |
Manuell |
| API Mocking ohne Plugin |
✓ page.route() |
✓ |
Nein |
| Trace Viewer |
✓ (eingebaut) |
Nein |
Nein |
| Test-Sharding |
✓ (nativ) |
Paid Feature |
Extern |
| Mobile Testing |
✓ (devices) |
Eingeschränkt |
✓ |
| Claude Code Integration |
✓ (optimiert) |
✓ |
Möglich |
CCTypischer Workflow mit Claude Code
So arbeiten Entwickler 2026 mit Playwright und Claude Code zusammen:
-
Feature beschreiben
Prompt: "Schreibe E2E-Tests für den Checkout-Flow: Produkt hinzufügen, Versandadresse eingeben, Zahlung mit Stripe simulieren, Bestellbestätigung prüfen."
-
POM generieren lassen
Claude Code analysiert die vorhandene Codebase und erstellt passende Page-Object-Klassen für CartPage, CheckoutPage und ConfirmationPage.
-
API-Mocks einrichten
"Mock die Stripe-API so, dass Zahlungen immer erfolgreich sind, und erstelle einen zweiten Test mit simuliertem Kartenablauffehler."
-
Visual Baseline erstellen
npx playwright test --update-snapshots — Baseline-Screenshots für alle neuen Tests erzeugen.
-
CI-Pipeline ergänzen
"Füge den neuen checkout.spec.ts zur GitHub Actions Pipeline hinzu, mit Sharding auf 4 Partitionen."
-
Fehler debuggen
Bei roten Tests: Trace-Datei dem Claude Code Agenten geben — er analysiert Timeline, Network und DOM automatisch.
Playwright-Tests mit KI automatisieren
Claude Code generiert vollständige E2E-Test-Suiten in Minuten — Locators, Page Object Model, API Mocks, Visual Tests und GitHub Actions Pipeline. Jetzt kostenlos ausprobieren.
Kostenlos testen — keine Kreditkarte
14 Tage Trial · Kein Abo · Sofort loslegen
Playwright
Claude Code
E2E Testing
TypeScript
Page Object Model
API Mocking
Visual Testing
GitHub Actions
CI/CD
Playwright Tracing
Veröffentlicht am 6. Mai 2026 · 11 min Lesezeit ·
← Alle Artikel