Quality Booster
Cheat Sheet Fortgeschritten

Playwright Best Practices

Das wichtigste auf einen Blick: Locator-Strategie, Debugging, Performance-Tipps und häufige Fehler – kompakt und sofort einsetzbar.

Kompakte PDF-Version Optimiert für A4-Druck
Playwright Testing Quick Reference

🎯 Die goldene Regel

Teste, was der Nutzer sieht – nicht die Implementierung.

// ✅ RICHTIG: Semantisch, robust
await page.getByRole('button', { name: 'Absenden' }).click();

// ❌ FALSCH: Bricht bei CSS-Änderungen
await page.click('#submit-btn.btn-primary');

🔍 Locator-Priorität (in dieser Reihenfolge)

PrioritätMethodeBeispielWann verwenden
1getByRole()getByRole('button', { name: 'Submit' })Immer zuerst – semantisch, ARIA-konform
2getByLabel()getByLabel('Passwort')Formularfelder
3getByPlaceholder()getByPlaceholder('E-Mail eingeben')Input-Hints
4getByText()getByText('Willkommen')Nicht-interaktive Elemente
5getByTestId()getByTestId('checkout-btn')Wenn nichts anderes geht

Nie verwenden: CSS-Selektoren, XPath, komplexe DOM-Pfade


✅ Web-First Assertions (mit Auto-Wait)

Playwright wartet automatisch bis zu 5 Sekunden (konfigurierbar):

// ✅ RICHTIG – wartet auf Sichtbarkeit
await expect(page.getByText('Erfolg')).toBeVisible();
await expect(page).toHaveTitle(/Dashboard/);
await expect(page.getByRole('listitem')).toHaveCount(3);

// ❌ FALSCH – sofortige Prüfung, kein Warten
expect(await page.getByText('Erfolg').isVisible()).toBe(true);

Häufige Assertions

// Sichtbarkeit & Status
await expect(locator).toBeVisible();
await expect(locator).toBeHidden();
await expect(locator).toBeEnabled();
await expect(locator).toBeDisabled();

// Inhalt & Werte
await expect(locator).toHaveText('Genauer Text');
await expect(locator).toContainText('Teiltext');
await expect(locator).toHaveValue('Eingabewert');
await expect(locator).toHaveAttribute('href', '/link');

// Anzahl & URL
await expect(page.getByRole('article')).toHaveCount(5);
await expect(page).toHaveURL('/dashboard');

⚡ Actions (Auto-Wait eingebaut)

Keine sleep() oder waitForTimeout() nötig:

// Navigation
await page.goto('https://example.com');

// Interaktionen
await page.getByRole('button').click();
await page.getByLabel('Benutzername').fill('andi');
await page.getByRole('checkbox').check();
await page.getByLabel('Land').selectOption('Deutschland');

// Fortgeschritten
await page.getByText('Menü').hover();
await page.getByRole('textbox').focus();
await page.keyboard.press('Enter');
await page.getByLabel('Upload').setInputFiles('datei.pdf');

🔗 Locator filtern & verketten

// Element innerhalb eines anderen finden
const produkt = page.getByRole('listitem').filter({ hasText: 'Produkt A' });
await produkt.getByRole('button', { name: 'In den Warenkorb' }).click();

// Nach abwesendem Text filtern
await expect(
  page.getByRole('listitem').filter({ hasNotText: 'Ausverkauft' })
).toHaveCount(5);

// Kombinierte Filter
await page
  .getByRole('listitem')
  .filter({ hasText: 'Max' })
  .filter({ has: page.getByRole('button', { name: 'Bearbeiten' }) })
  .click();

🛠️ Debugging & Entwicklung

# Test-Generator (Locator aufzeichnen)
npx playwright codegen example.com

# Mit Inspector debuggen
npx playwright test --debug

# Trace für CI aktivieren
npx playwright test --trace on
npx playwright show-report

# Einzelnen Test debuggen
npx playwright test test.spec.ts:42 --debug

Nützliche Debugging-Config

// playwright.config.ts
export default defineConfig({
  use: {
    // Lokal: vollständiger Trace
    trace: 'on',
    // CI: nur bei Retry
    // trace: 'on-first-retry',
    
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
});

📋 Test-Struktur

import { test, expect } from '@playwright/test';

// Parallele Ausführung in dieser Datei
test.describe.configure({ mode: 'parallel' });

test.describe('Authentifizierung', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('erfolgreicher Login', async ({ page }) => {
    await page.getByLabel('E-Mail').fill('test@example.com');
    await page.getByLabel('Passwort').fill('geheim');
    await page.getByRole('button', { name: 'Anmelden' }).click();
    
    await expect(page).toHaveURL('/dashboard');
    await expect(page.getByText('Willkommen zurück')).toBeVisible();
  });

  test('fehlgeschlagener Login zeigt Fehler', async ({ page }) => {
    await page.getByLabel('E-Mail').fill('falsch@example.com');
    await page.getByRole('button', { name: 'Anmelden' }).click();
    
    await expect(page.getByText('Ungültige Anmeldedaten')).toBeVisible();
  });
});

⚙️ Performance-Optimierung

// playwright.config.ts
export default defineConfig({
  // Worker parallelisieren
  workers: process.env.CI ? 4 : undefined,
  
  // Wiederholungen bei Flakiness
  retries: process.env.CI ? 2 : 0,
  
  // Projekte für verschiedene Browser
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
});

🚨 Häufige Fehler

FehlerLösung
sleep(1000) verwendenAuto-wait nutzen, expect().toBeVisible()
CSS-Selektoren wie #btn-123getByRole() oder getByTestId()
Tests hängen vom Zustand anderer Tests abtest.beforeEach für Isolation
Keine Assertions nach AktionImmer expect() verwenden
Hartkodierte Timeoutsexpect(...).toBeVisible({ timeout: 10000 })
Externe Seiten testenMit page.route() mocken


Dieses Cheat Sheet wird regelmäßig aktualisiert. Letzte Aktualisierung: April 2026

Mehr praktische Ressourcen im Blog

Zur Kompakt-Version →