Zum Inhalt springen
Anleitungen

Playwright - Automatisierte E2E-Tests für Webanwendungen

Veröffentlicht am:
·7 Min. Lesezeit·Autor: MDS Software Solutions Group

Playwright Automatisierte E2E-Tests

poradniki

Playwright - E2E-Tests für Webanwendungen

End-to-End-Tests (E2E) sind ein entscheidender Bestandteil der Qualitätssicherung von Webanwendungen. Playwright, entwickelt von Microsoft, hat sich schnell zu einem der beliebtesten Frameworks für die Browser-Automatisierung entwickelt. Es bietet Multi-Browser-Unterstützung, intelligentes Auto-Waiting, leistungsstarke Locators und eine Vielzahl von Entwicklerwerkzeugen. In diesem Leitfaden zeigen wir Ihnen, wie Sie die Möglichkeiten von Playwright in TypeScript-basierten Projekten voll ausschöpfen können.

Was ist Playwright?#

Playwright ist ein modernes E2E-Test-Automatisierungsframework, mit dem Sie Webanwendungen in Chromium-, Firefox- und WebKit-Browsern über eine einzige API testen können. Im Gegensatz zu vielen älteren Tools wurde Playwright von Grund auf für moderne Single-Page-Anwendungen entwickelt und unterstützt nativ asynchrone Operationen, Shadow DOM, iframes und mehrere Tabs.

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

test('Startseite zeigt den richtigen Titel', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example/);
});

Die Installation ist denkbar einfach:

npm init playwright@latest

Dieser Befehl erstellt eine vollständige Projektstruktur mit einer playwright.config.ts-Konfigurationsdatei, einem Testverzeichnis und einem Beispieltest für den Einstieg.

Playwright vs Cypress vs Selenium#

Die Wahl eines E2E-Testtools kann herausfordernd sein. Vergleichen wir die drei beliebtesten Lösungen.

Selenium ist der Veteran der Browser-Automatisierung. Es unterstützt viele Programmiersprachen und verfügt über eine riesige Community. Allerdings führt seine WebDriver-basierte Architektur oft zu langsamen und instabilen Tests. Die Konfiguration ist komplex und die API wirkt teilweise umständlich.

Cypress hat das E2E-Testing mit einer hervorragenden Developer Experience revolutioniert. Es läuft direkt im Browser und liefert schnelles Feedback. Seine Einschränkungen umfassen die Unterstützung nur für Chromium-basierte Browser (mit experimenteller Firefox-Unterstützung), keine Multi-Tab-Unterstützung und eine synchrone Architektur, die das Testen komplexer Szenarien erschwert.

Playwright vereint das Beste aus beiden Welten. Es bietet Unterstützung für alle gängigen Browser, native async/await-Muster, Multi-Tab-Testing, Browser-Context-Isolation und integrierte Entwicklerwerkzeuge. Es ist schneller als Selenium und flexibler als Cypress.

// Playwright - Multi-Browser-Testing in einer einzigen Konfigurationsdatei
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
    { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
  ],
});

Multi-Browser-Testing#

Einer der größten Vorteile von Playwright ist die native Multi-Browser-Unterstützung. Jeder Browser wird bei der Installation automatisch heruntergeladen und kann mit einem einzigen Befehl aktualisiert werden:

npx playwright install

Playwright unterstützt drei Rendering-Engines: Chromium (Chrome, Edge), Firefox (Gecko) und WebKit (Safari). Das bedeutet, dass Sie Ihre Anwendung auf Engines testen können, die über 95 % des Browsermarktes abdecken.

Tests, die in mehreren Browsern laufen, werden in der playwright.config.ts durch die Definition von Projekten konfiguriert. Jedes Projekt kann eigene Einstellungen haben, wie Viewport-Größe, mobile Geräteemulation oder Geolokalisierung.

export default defineConfig({
  projects: [
    {
      name: 'Desktop Chrome',
      use: {
        browserName: 'chromium',
        viewport: { width: 1920, height: 1080 },
      },
    },
    {
      name: 'Mobile Safari',
      use: {
        browserName: 'webkit',
        viewport: { width: 390, height: 844 },
        isMobile: true,
        hasTouch: true,
      },
    },
  ],
});

Auto-Waiting - Intelligente Elementsynchronisation#

Eines der häufigsten Probleme bei E2E-Tests ist die Instabilität durch manuelles Warten auf Elemente. Playwright löst dieses Problem mit seinem Auto-Waiting-Mechanismus, der automatisch wartet, bis Elemente bestimmte Bedingungen erfüllen, bevor eine Aktion ausgeführt wird.

Wenn Sie page.click('button') aufrufen, wartet Playwright automatisch, bis der Button sichtbar, aktiviert, stabil (nicht animiert), nicht von anderen Elementen verdeckt und bereit ist, Events zu empfangen. Dies reduziert die Anzahl instabiler Tests drastisch.

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

  // Playwright wartet automatisch, bis die Felder bereit sind
  await page.fill('#email', 'user@example.com');
  await page.fill('#password', 'securePassword123');
  await page.click('button[type="submit"]');

  // Auto-Waiting funktioniert auch bei Assertions
  await expect(page.locator('.dashboard')).toBeVisible();
  await expect(page).toHaveURL('/dashboard');
});

Sie müssen in den meisten Fällen keine künstlichen Verzögerungen oder explizite waitFor-Aufrufe hinzufügen. Playwright kümmert sich automatisch um die Synchronisation.

Locators - Präzise Elementauswahl#

Locators sind das Herzstück der Elementinteraktion in Playwright. Im Gegensatz zu einfachen CSS-Selektoren oder XPath sind Playwright-Locators "lazy" - sie suchen das Element nicht sofort, sondern erst zum Zeitpunkt der Aktionsausführung. Das macht sie widerstandsfähig gegen DOM-Änderungen.

test('Produktsuche', async ({ page }) => {
  await page.goto('/products');

  // ARIA-Rollen-Locator - Best Practice
  const searchInput = page.getByRole('searchbox', { name: 'Produkte suchen' });
  await searchInput.fill('laptop');

  // Text-Locator
  const submitButton = page.getByRole('button', { name: 'Suchen' });
  await submitButton.click();

  // Test-ID-Locator - wenn andere Methoden nicht passen
  const resultsList = page.getByTestId('search-results');
  await expect(resultsList).toBeVisible();

  // Locators verketten - Filtern
  const firstProduct = page.getByRole('listitem')
    .filter({ hasText: 'Laptop Pro' })
    .first();
  await expect(firstProduct).toContainText('Laptop Pro');

  // Placeholder-Locator
  const filterInput = page.getByPlaceholder('Ergebnisse filtern...');
  await filterInput.fill('16GB RAM');
});

Playwright empfiehlt die Verwendung von Locators basierend auf ARIA-Rollen (getByRole), Labels (getByLabel), Text (getByText) und Testattributen (getByTestId). Dieser Ansatz macht Tests widerstandsfähiger gegen Änderungen in der HTML-Struktur und überprüft gleichzeitig die Barrierefreiheit Ihrer Anwendung.

Assertions - Überprüfung des Anwendungszustands#

Playwright bietet einen umfangreichen Satz an Assertions, die automatisch wiederholen, bis die Bedingung erfüllt ist oder das Timeout abläuft. Diese sogenannten "Web-First Assertions" beseitigen das Problem falsch-negativer Ergebnisse.

test('Warenkorb', async ({ page }) => {
  await page.goto('/cart');

  // Element-Assertions
  const cartHeader = page.getByRole('heading', { name: 'Warenkorb' });
  await expect(cartHeader).toBeVisible();
  await expect(cartHeader).toHaveText('Warenkorb (3 Artikel)');

  // Listen-Assertions
  const items = page.getByRole('listitem');
  await expect(items).toHaveCount(3);

  // CSS-Attribut-Assertions
  const totalPrice = page.getByTestId('total-price');
  await expect(totalPrice).toHaveCSS('font-weight', '700');
  await expect(totalPrice).toContainText('EUR');

  // Seiten-Assertions
  await expect(page).toHaveURL(/\/cart/);
  await expect(page).toHaveTitle('Warenkorb - Mein Shop');

  // Negations-Assertions
  const emptyMessage = page.getByText('Ihr Warenkorb ist leer');
  await expect(emptyMessage).not.toBeVisible();

  // Input-Wert-Assertions
  const quantityInput = page.getByLabel('Menge').first();
  await expect(quantityInput).toHaveValue('1');
});

Page Object Model - Muster zur Testorganisation#

Das Page Object Model (POM) ist ein Entwurfsmuster, das Seiteninteraktionen in dedizierten Klassen kapselt. Dadurch werden Tests lesbarer, einfacher zu warten und widerstandsfähiger gegen UI-Änderungen.

// pages/login.page.ts
import { type Page, type Locator, expect } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel('E-Mail');
    this.passwordInput = page.getByLabel('Passwort');
    this.submitButton = page.getByRole('button', { name: 'Anmelden' });
    this.errorMessage = page.getByRole('alert');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectError(message: string) {
    await expect(this.errorMessage).toContainText(message);
  }

  async expectSuccessRedirect() {
    await expect(this.page).toHaveURL('/dashboard');
  }
}

// tests/login.spec.ts
import { test } from '@playwright/test';
import { LoginPage } from '../pages/login.page';

test.describe('Anmeldung', () => {
  let loginPage: LoginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.goto();
  });

  test('erfolgreiche Anmeldung', async () => {
    await loginPage.login('user@example.com', 'password123');
    await loginPage.expectSuccessRedirect();
  });

  test('falsches Passwort zeigt Fehlermeldung', async () => {
    await loginPage.login('user@example.com', 'wrong');
    await loginPage.expectError('Ungültige Anmeldedaten');
  });
});

Fixtures - Verwaltung des Testzustands#

Fixtures in Playwright ermöglichen die Konfiguration und gemeinsame Nutzung von Ressourcen zwischen Tests. Sie können eigene Fixtures erstellen, die automatisch Page Objects initialisieren, Benutzer einloggen, Testdaten vorbereiten oder Mocks konfigurieren.

// fixtures/base.fixture.ts
import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/login.page';
import { DashboardPage } from '../pages/dashboard.page';

type MyFixtures = {
  loginPage: LoginPage;
  dashboardPage: DashboardPage;
  authenticatedPage: DashboardPage;
};

export const test = base.extend<MyFixtures>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await use(loginPage);
  },

  dashboardPage: async ({ page }, use) => {
    await use(new DashboardPage(page));
  },

  authenticatedPage: async ({ page }, use) => {
    // Automatische Anmeldung vor jedem Test
    await page.goto('/login');
    await page.getByLabel('E-Mail').fill('admin@example.com');
    await page.getByLabel('Passwort').fill('admin123');
    await page.getByRole('button', { name: 'Anmelden' }).click();
    await page.waitForURL('/dashboard');

    const dashboardPage = new DashboardPage(page);
    await use(dashboardPage);
  },
});

export { expect } from '@playwright/test';

// tests/dashboard.spec.ts
import { test, expect } from '../fixtures/base.fixture';

test('angemeldeter Benutzer sieht Dashboard', async ({ authenticatedPage }) => {
  await expect(authenticatedPage.welcomeMessage).toContainText('Willkommen');
});

Die Fixture authenticatedPage meldet den Benutzer automatisch vor jedem Test an, der sie verwendet. Das eliminiert Code-Duplikation und stellt einen konsistenten Ausgangszustand sicher.

API-Tests#

Playwright beschränkt sich nicht auf UI-Tests. Der integrierte APIRequestContext ermöglicht HTTP-Anfragen, was nützlich ist, um Testdaten vorzubereiten, den Backend-Zustand zu überprüfen oder die API selbst zu testen.

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

test.describe('Produkte-API', () => {
  test('GET /api/products gibt eine Produktliste zurück', async ({ request }) => {
    const response = await request.get('/api/products');

    expect(response.status()).toBe(200);

    const products = await response.json();
    expect(products).toHaveLength(10);
    expect(products[0]).toHaveProperty('name');
    expect(products[0]).toHaveProperty('price');
  });

  test('POST /api/products erstellt ein neues Produkt', async ({ request }) => {
    const newProduct = {
      name: 'Neues Produkt',
      price: 99.99,
      category: 'electronics',
    };

    const response = await request.post('/api/products', {
      data: newProduct,
    });

    expect(response.status()).toBe(201);
    const created = await response.json();
    expect(created.name).toBe('Neues Produkt');
    expect(created.id).toBeDefined();
  });

  test('Daten per API vorbereiten, im UI verifizieren', async ({ page, request }) => {
    // Produkt über API erstellen
    const response = await request.post('/api/products', {
      data: { name: 'Test-Laptop', price: 4999.00, category: 'laptops' },
    });
    const product = await response.json();

    // Überprüfung in der Benutzeroberfläche
    await page.goto(`/products/${product.id}`);
    await expect(page.getByRole('heading')).toContainText('Test-Laptop');
    await expect(page.getByTestId('price')).toContainText('4.999,00 €');
  });
});

Visuelle Vergleiche (Visual Comparisons)#

Playwright bietet integriertes visuelles Vergleichen (Snapshot-Testing), das unbeabsichtigte Änderungen im Erscheinungsbild der Seite erkennt. Beim ersten Durchlauf werden Referenz-Screenshots erstellt, und bei nachfolgenden Durchläufen werden sie mit dem aktuellen Zustand verglichen.

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

test('Aussehen der Startseite', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png');
});

test('Aussehen der Produktkarten-Komponente', async ({ page }) => {
  await page.goto('/products');

  const productCard = page.getByTestId('product-card').first();
  await expect(productCard).toHaveScreenshot('product-card.png', {
    maxDiffPixelRatio: 0.05, // 5% Toleranz
  });
});

test('responsives Aussehen auf Mobilgeräten', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 812 });
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage-mobile.png');
});

test('Vergleich mit Maskierung dynamischer Inhalte', async ({ page }) => {
  await page.goto('/dashboard');

  await expect(page).toHaveScreenshot('dashboard.png', {
    mask: [
      page.getByTestId('current-date'),
      page.getByTestId('random-banner'),
    ],
  });
});

Um Referenz-Screenshots nach beabsichtigten UI-Änderungen zu aktualisieren, genügt folgender Befehl:

npx playwright test --update-snapshots

Trace Viewer - Tests debuggen#

Der Trace Viewer ist eines der leistungsstärksten Werkzeuge von Playwright. Er zeichnet eine vollständige Spur der Testausführung auf, einschließlich Screenshots bei jedem Schritt, DOM-Snapshots, Netzwerk-Logs und Konsolenausgaben. Damit wird das Debuggen selbst komplexester Szenarien intuitiv.

// playwright.config.ts
export default defineConfig({
  use: {
    // Trace nur bei Fehlern aufzeichnen
    trace: 'on-first-retry',

    // Oder immer aufzeichnen (nützlich in CI)
    // trace: 'on',
  },
});

Nach dem Ausführen der Tests mit Trace-Aufzeichnung kann die .zip-Datei im Browser geöffnet werden:

npx playwright show-trace trace.zip

Der Trace Viewer zeigt eine chronologische Liste der Aktionen mit entsprechenden Screenshots, ermöglicht die Inspektion des DOM-Zustands vor und nach jeder Aktion, zeigt Details zu Netzwerkanfragen und Konsolenausgaben. Er ist ein unverzichtbares Werkzeug für die Fehleranalyse in CI-Umgebungen, in denen Sie keinen direkten Browserzugriff haben.

Test-Generator (Codegen)#

Playwright Codegen ist ein interaktiver Testgenerator, der Ihre Browser-Interaktionen aufzeichnet und automatisch Testcode generiert. Er ist eine hervorragende Möglichkeit, schnell Testgerüste zu erstellen.

npx playwright codegen https://example.com

Nach dem Ausführen dieses Befehls öffnet sich ein Browser mit einem Inspektor-Tool. Jeder Klick, jede Texteingabe und jede Navigation wird automatisch in Playwright-Code übersetzt. Der Generator wählt intelligent Locators aus und bevorzugt dabei ARIA-Rollen und Testattribute.

Codegen unterstützt auch die Codegenerierung zur Aufzeichnung des Authentifizierungszustands:

npx playwright codegen --save-storage=auth.json https://example.com/login

Die aufgezeichneten Authentifizierungsdaten können anschließend in Tests wiederverwendet werden, wodurch die Notwendigkeit entfällt, sich bei jedem Test anzumelden:

export default defineConfig({
  projects: [
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: 'tests',
      dependencies: ['setup'],
      use: {
        storageState: 'playwright/.auth/user.json',
      },
    },
  ],
});

CI/CD-Integration#

Playwright lässt sich nahtlos in gängige CI/CD-Systeme integrieren. Die offizielle Dokumentation bietet fertige Konfigurationen für GitHub Actions, GitLab CI, Azure Pipelines und viele weitere.

# .github/workflows/e2e-tests.yml
name: E2E 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

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run Playwright tests
        run: npx playwright test

      - name: Upload test results
        uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Die wichtige Option in CI ist --with-deps, die Systemabhängigkeiten installiert, die von den Browsern benötigt werden. Damit entfällt die manuelle Umgebungskonfiguration.

Parallele Testausführung#

Playwright führt Testdateien standardmäßig parallel aus und nutzt dabei mehrere Worker-Prozesse. Tests innerhalb einer einzelnen Datei werden sequenziell ausgeführt, es sei denn, Sie konfigurieren es explizit anders.

// playwright.config.ts
export default defineConfig({
  // Anzahl paralleler Worker
  workers: process.env.CI ? 2 : undefined, // In CI begrenzen, lokal automatisch

  // Volle Parallelität - auch Tests innerhalb einer Datei
  fullyParallel: true,

  // Fehlgeschlagene Tests wiederholen
  retries: process.env.CI ? 2 : 0,

  // Timeout pro einzelnem Test
  timeout: 30_000,
});

Damit Tests parallel laufen können, müssen sie voneinander unabhängig sein. Jeder Test sollte mit eigenen Daten arbeiten und sich nicht auf den Zustand anderer Tests verlassen. Playwright erleichtert dies durch Browser-Context-Isolation - jeder Test erhält standardmäßig einen frischen Kontext.

test.describe('parallele Tests', () => {
  test.describe.configure({ mode: 'parallel' });

  test('Test A', async ({ page }) => {
    // Läuft parallel zu Test B
    await page.goto('/feature-a');
    await expect(page.getByRole('heading')).toContainText('Feature A');
  });

  test('Test B', async ({ page }) => {
    // Läuft parallel zu Test A
    await page.goto('/feature-b');
    await expect(page.getByRole('heading')).toContainText('Feature B');
  });
});

Berichterstattung#

Playwright bietet mehrere integrierte Reporter und die Möglichkeit, eigene zu erstellen. Am beliebtesten ist der HTML-Reporter, der einen interaktiven Bericht mit Testergebnissen generiert.

// playwright.config.ts
export default defineConfig({
  reporter: [
    // HTML-Reporter - interaktiver Bericht
    ['html', { outputFolder: 'playwright-report', open: 'never' }],

    // Konsolen-Reporter - Testliste
    ['list'],

    // JUnit - CI/CD-Integration
    ['junit', { outputFile: 'results/junit.xml' }],

    // JSON - zur Weiterverarbeitung
    ['json', { outputFile: 'results/results.json' }],
  ],
});

Nach dem Ausführen der Tests kann der HTML-Bericht geöffnet werden mit:

npx playwright show-report

Der HTML-Bericht enthält detaillierte Informationen zu jedem Test: Ausführungszeit, Screenshots, Logs, Traces und Fehlerdetails. Er ist ein ideales Werkzeug zur Analyse von Ergebnissen in CI/CD und zum Teilen mit Ihrem Team.

Best Practices#

Fassen wir die wichtigsten Best Practices für die Arbeit mit Playwright zusammen:

  1. Verwenden Sie ARIA-rollenbasierte Locators - getByRole, getByLabel, getByText anstelle von CSS-Selektoren.
  2. Keine künstlichen Verzögerungen - verlassen Sie sich auf Auto-Waiting und Web-First Assertions.
  3. Isolieren Sie Tests - jeder Test sollte unabhängig sein und in einem sauberen Kontext laufen.
  4. Verwenden Sie das Page Object Model - kapseln Sie Seitenlogik in dedizierten Klassen.
  5. Daten über API vorbereiten - nutzen Sie die request-Fixture für schnelle Testdatenerstellung.
  6. Traces in CI aufzeichnen - konfigurieren Sie trace: 'on-first-retry' zum Debuggen von Fehlern.
  7. Tests parallel ausführen - entwerfen Sie Tests als unabhängig, um volle Parallelität zu nutzen.
  8. Tests taggen - verwenden Sie test.describe und Tags zur Organisation von Testsuiten.

Fazit#

Playwright ist ein leistungsstarkes und ausgereiftes Werkzeug, das die Qualität des Testens von Webanwendungen erheblich steigert. Mit Multi-Browser-Unterstützung, intelligentem Auto-Waiting, leistungsstarken Locators, integriertem Trace Viewer und einem Testgenerator ermöglicht es das Schreiben stabiler und wartbarer E2E-Tests. CI/CD-Integration, parallele Ausführung und umfassende Berichterstattung machen Playwright zur perfekten Ergänzung moderner Softwareentwicklungsprozesse.


Benötigen Sie Hilfe bei der Implementierung von E2E-Tests in Ihrem Projekt? MDS Software Solutions Group ist spezialisiert auf den Aufbau umfassender Teststrategien, QA-Automatisierung und CI/CD-Pipeline-Integration. Kontaktieren Sie uns, um zu erfahren, wie wir Ihrem Team helfen können, Software höchster Qualität zu liefern.

Autor
MDS Software Solutions Group

Team von Programmierexperten, die sich auf moderne Webtechnologien spezialisiert haben.

Playwright - Automatisierte E2E-Tests für Webanwendungen | MDS Software Solutions Group | MDS Software Solutions Group