Quality Booster

Playwright Tutorial: Vom ersten Test bis zur CI/CD-Integration

Das umfassende Playwright-Tutorial für Einsteiger. Lerne Schritt-für-Schritt, wie du stabile Browser-Tests erstellst, automatisch wartest und in deine Pipeline integrierst – mit praktischen Code-Beispielen.

Andi

Andi

Test-Manager

Playwright Tutorial: Vom ersten Test bis zur CI/CD-Integration

Du willst deine Web-Applikation automatisch testen, hast aber genug von flackenden Tests und kompliziertem Setup? Ich zeige dir, warum Playwright aktuell das beste Tool für E2E-Testing ist – und wie du in unter einer Stunde deinen ersten stabilen Test laufen lässt.

Warum Playwright (und nicht Selenium oder Cypress)?

Nach 15 Jahren mit Selenium und zwei Jahren mit Cypress bin ich zu Playwright gewechselt. Hier ist der ehrliche Vergleich:

FeaturePlaywrightCypressSelenium
Auto-Waiting✅ Eingebaut⚠️ Teilweise❌ Manuell
Multi-Browser✅ Chrome, Firefox, Safari, Edge⚠️ Electron-basiert✅ Ja, aber umständlich
Parallelisierung✅ Eingebaut⚠️ Nur mit Dashboard⚠️ Grid nötig
Mobile Emulation✅ Eingebaut❌ Nicht möglich⚠️ Komplex
Trace/Debugger✅ Hervorragend✅ Gut❌ Mangelhaft
API Testing✅ Eingebaut⚠️ Umständlich❌ Nicht eingebaut

Mein Fazit: Playwright kombiniert die Zuverlässigkeit von Selenium mit der Entwickler-Erfahrung von Cypress – und verbessert beides.

Installation und Setup (5 Minuten)

Voraussetzungen

  • Node.js 16+ (prüfe mit node --version)
  • Ein Terminal deiner Wahl

Schritt 1: Playwright installieren

# Neues Projekt erstellen (optional)
mkdir meine-playwright-tests
cd meine-playwright-tests

# Playwright initialisieren
npm init playwright@latest

Der Installer fragt dich nach:

  • TypeScript oder JavaScript: Ich empfehle TypeScript für bessere Autocomplete-Unterstützung
  • Tests-Verzeichnis: tests (Standard, akzeptieren)
  • CI/CD Workflow: GitHub Actions (optional, kannst du später hinzufügen)

Schritt 2: Browser installieren

npx playwright install

Das lädt Chromium, Firefox und WebKit herunter – insgesamt ca. 100 MB pro Browser.

Schritt 3: Testlauf

npx playwright test

Wenn alles funktioniert, siehst du die Playwright-Tests im Headless-Modus durchlaufen.

Dein erster Test: Login-Funktion

Lass uns einen realistischen Test schreiben – einen Login-Flow, wie er in jeder Web-App vorkommt.

Die Anwendung

Angenommen, du hast eine Login-Seite mit:

  • Eingabefeld für E-Mail
  • Eingabefeld für Passwort
  • Submit-Button
  • Erfolgsmeldung nach Login

Der Test-Code

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

test('erfolgreicher Login', async ({ page }) => {
  // Arrange: Seite öffnen
  await page.goto('https://meine-app.de/login');
  
  // Act: Login-Daten eingeben
  await page.getByLabel('E-Mail').fill('test@example.com');
  await page.getByLabel('Passwort').fill('geheim123');
  await page.getByRole('button', { name: 'Anmelden' }).click();
  
  // Assert: Erfolg prüfen
  await expect(page.getByText('Willkommen zurück')).toBeVisible();
});

Was ist hier anders als bei Selenium?

  • Keine sleep() oder waitFor() nötig – Playwright wartet automatisch
  • getByLabel() und getByRole() statt CSS-Selektoren – robuster gegen UI-Änderungen
  • Der Test läuft deterministisch, auch bei langsamer Verbindung

Die wichtigsten Locator-Strategien

Playwright empfiehlt diese Reihenfolge für Selektoren (von robust bis fragile):

1. getByRole (Empfohlen)

await page.getByRole('button', { name: 'Absenden' }).click();
await page.getByRole('heading', { name: 'Dashboard' });
await page.getByRole('textbox', { name: 'E-Mail' }).fill('test@example.com');

Warum: Ändert sich das Design, überlebt der Test solange die Bedeutung erhalten bleibt.

2. getByLabel

await page.getByLabel('Vorname').fill('Max');
await page.getByLabel('Ich stimme den AGB zu').check();

Warum: Verknüpft sich mit dem <label>-Element – sehr stabil.

3. getByTestId (Wenn nichts anderes geht)

// HTML: <button data-testid="submit-order">Bestellen</button>
await page.getByTestId('submit-order').click();

Warum: Entwickler haben volle Kontrolle, aber erfordert Eingreifen ins HTML.

4. CSS-Selektoren (Vermeiden)

// Fragil – bricht bei Design-Änderungen
await page.locator('.btn-primary').click();
await page.locator('#email-input').fill('test@example.com');

Warten ohne Warten: Auto-Waiting

Playwrights Killer-Feature ist das automatische Warten. Vergleiche:

❌ Alte Art (Selenium/Cypress)

// Manuell warten – ineffizient
await page.waitForTimeout(2000); // Hartes Warten
await page.waitForSelector('.loaded'); // Explizit warten
await page.click('.button');

✅ Playwright-Art

// Automatisch warten – robust
await page.getByRole('button', { name: 'Bestellen' }).click();
// Playwright wartet bis der Button sichtbar UND klickbar ist

Was Playwright automatisch prüft:

  • Element ist im DOM
  • Element ist sichtbar
  • Element ist nicht behindert (z.B. durch Overlay)
  • Element ist aktiviert (nicht disabled)

Das spart dir hunderte Zeilen Boilerplate-Code.

Test-Daten und Umgebungen

Fixtures: Wiederverwendbare Setups

// fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  // Eingeloggter User für jeden Test
  loggedInPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.getByLabel('E-Mail').fill(process.env.TEST_USER!);
    await page.getByLabel('Passwort').fill(process.env.TEST_PASSWORD!);
    await page.getByRole('button', { name: 'Anmelden' }).click();
    await use(page);
  },
});

Verwendung:

import { test, expect } from './fixtures';

test('Warenkorb mit eingeloggtem User', async ({ loggedInPage }) => {
  // Page ist bereits eingeloggt
  await loggedInPage.goto('/shop');
  await loggedInPage.getByText('In den Warenkorb').click();
  // ...
});

Umgebungsvariablen

Erstelle eine .env Datei:

BASE_URL=https://staging.meine-app.de
TEST_USER=test@example.com
TEST_PASSWORD=geheim123

Und nutze sie in playwright.config.ts:

import { defineConfig } from '@playwright/test';
import dotenv from 'dotenv';

dotenv.config();

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL,
  },
});

Debugging: Wenn Tests fehlschlagen

Playwrights Debugging-Tools sind erstklassig.

Trace Viewer

# Mit Trace aufzeichnen
npx playwright test --trace on

# Interaktiv ansehen
npx playwright show-report

Der Trace zeigt dir:

  • Screenshots vor/nach jedem Schritt
  • DOM-Änderungen
  • Netzwerk-Requests
  • Console-Logs

Test im sichtbaren Browser laufen lassen

# Chromium mit UI
npx playwright test --headed

# Spezifischer Browser
npx playwright test --project=firefox --headed

VS Code Extension

Installiere die offizielle Playwright-Extension:

  • Breakpoints in Tests setzen
  • Schritt-für-Schritt durchgehen
  • Element-Inspector nutzen

CI/CD Integration

Playwright ist für CI/CD optimiert.

GitHub Actions

name: Playwright Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/

Wichtig: Das --with-deps Flag installiert System-Abhängigkeiten für Browser.

Parallelisierung

Playwright läuft out-of-the-box parallel:

// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 4 : undefined, // 4 parallele Worker in CI
  retries: process.env.CI ? 2 : 0, // 2 Versuche in CI bei Flakiness
});

Best Practices aus der Praxis

1. Tests isoliert halten

Jeder Test sollte unabhängig sein – keine Reihenfolge-Abhängigkeiten.

// ❌ Schlecht: Test B hängt von Test A ab
test('Test A: User anlegen', async () => { /* ... */ });
test('Test B: User löschen', async () => { /* verwendet User aus A */ });

// ✅ Gut: Jeder Test bereitet sein Setup selbst vor
test('User-Workflow', async () => {
  const user = await createUser(); // Im Test anlegen
  await deleteUser(user.id); // Und wieder aufräumen
});

2. API-Calls für Setup nutzen

Nutze Playwrights API-Testing für schnelleres Setup:

test('Einkauf abschließen', async ({ page, request }) => {
  // Schneller: API statt UI für Setup
  await request.post('/api/cart', { data: { productId: '123', quantity: 2 }});
  
  // Nur den relevanten Teil über UI testen
  await page.goto('/checkout');
  await page.getByRole('button', { name: 'Kaufen' }).click();
});

3. Sensible Daten maskieren

// In Reports wird das Passwort durch *** ersetzt
await page.getByLabel('Passwort').fill(process.env.TEST_PASSWORD!);

4. Flakiness vermeiden

// ❌ Timing-basiert
await page.waitForTimeout(1000);

// ✅ Zustands-basiert
await expect(page.getByText('Gespeichert')).toBeVisible();

Nächste Schritte

Du hast jetzt ein solides Fundament. Hier sind die nächsten Themen:

  1. API-Testing mit Playwright: Gleiches Tool für Frontend und Backend
  2. Visual Regression: Screenshot-Vergleiche für UI-Konsistenz
  3. Komponenten-Tests: Einzelne UI-Komponenten isoliert testen
  4. Mobile Testing: Responsive Design auf verschiedenen Viewports testen

Zusammenfassung

Was du heute gelernt hast:

✅ Playwright in 5 Minuten installiert
✅ Deinen ersten stabilen Login-Test geschrieben
✅ Robuste Locator-Strategien kennengelernt
✅ Verstanden, warum Auto-Waiting Zeit spart
✅ Fixtures für wiederverwendbare Setups genutzt
✅ Debugging-Tools für schnelle Fehlersuche
✅ CI/CD Integration eingerichtet

Dein nächster Schritt: Schreibe einen Test für deine eigene Applikation. Fang klein an – ein einzelner Happy-Path-Test ist besser als keiner.

Hast du Fragen oder läuft etwas nicht? Schreib mir – ich helfe gerne weiter.

Andi

Andi

Test-Manager