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
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:
| Feature | Playwright | Cypress | Selenium |
|---|---|---|---|
| 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()oderwaitFor()nötig – Playwright wartet automatisch getByLabel()undgetByRole()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:
- API-Testing mit Playwright: Gleiches Tool für Frontend und Backend
- Visual Regression: Screenshot-Vergleiche für UI-Konsistenz
- Komponenten-Tests: Einzelne UI-Komponenten isoliert testen
- 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
Test-Manager