Playwright-Testleitfaden: E2E-Tests für Next.js-Anwendungen

Schreiben Sie mit Playwright zuverlässige End-to-End-Tests für Next.js-Anwendungen. Vollständige Anleitung zu Einrichtung, Seitenobjekten, Authentifizierungstests, visueller Regression und CI/CD-Integration.

E

ECOSIRE Research and Development Team

ECOSIRE-Team

5. März 20265 Min. Lesezeit1.1k Wörter

Playwright-Testleitfaden: E2E-Tests für Next.js-Anwendungen

End-to-End-Tests fangen die Fehler auf, die Unit-Tests übersehen – unterbrochene Authentifizierungsflüsse, Fehler bei der Formularübermittlung, Navigationsfehler und browserübergreifende Rendering-Probleme. Playwright, entwickelt von Microsoft, hat sich mit seiner Multi-Browser-Unterstützung, automatischen Wartefunktionen und leistungsstarken Debugging-Tools zum führenden E2E-Test-Framework entwickelt.

Wichtige Erkenntnisse

– Playwright-Tests werden in einer einzigen Testsuite für Chromium, Firefox und WebKit ausgeführt – Automatisches Warten eliminiert flockige Tests, indem vor der Interaktion darauf gewartet wird, dass Elemente umsetzbar sind – Der Authentifizierungsstatus kann für eine schnellere Ausführung von mehreren Tests gemeinsam genutzt werden – Die CI-Integration mit GitHub Actions ermöglicht automatisierte Tests bei jeder Pull-Anfrage


Projekt-Setup

Installation

npm init playwright@latest

Dadurch wird Playwright installiert, eine Konfigurationsdatei erstellt und Browser-Binärdateien heruntergeladen. Konfigurieren Sie für Next.js-Projekte den Webserver so, dass er automatisch startet:

// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./e2e",
  timeout: 30000,
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: "http://localhost:3000",
    screenshot: "only-on-failure",
    trace: "on-first-retry",
  },
  webServer: {
    command: "npm run dev",
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
  projects: [
    { name: "chromium", use: { browserName: "chromium" } },
    { name: "firefox", use: { browserName: "firefox" } },
    { name: "webkit", use: { browserName: "webkit" } },
  ],
});

Schreiben Sie Ihren ersten Test

Grundlegender Navigationstest

// e2e/navigation.spec.ts
import { test, expect } from "@playwright/test";

test("home page loads and displays title", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveTitle(/ECOSIRE/);
  await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
});

test("navigation to services page", async ({ page }) => {
  await page.goto("/");
  await page.getByRole("link", { name: "Services" }).click();
  await expect(page).toHaveURL(/services/);
  await expect(page.getByRole("heading", { name: /Services/ })).toBeVisible();
});

Formulareinreichungstest

test("contact form submission", async ({ page }) => {
  await page.goto("/contact");

  await page.getByLabel("Name").fill("Test User");
  await page.getByLabel("Email").fill("[email protected]");
  await page.getByLabel("Message").fill("This is a test message");
  await page.getByRole("button", { name: "Send" }).click();

  await expect(page.getByText("Message sent")).toBeVisible();
});

Seitenobjektmuster

Kapseln Sie Seiteninteraktionen zur Wartbarkeit in Seitenobjekte:

// e2e/pages/login.page.ts
import { Page, expect } from "@playwright/test";

export class LoginPage {
  constructor(private page: Page) {}

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

  async login(email: string, password: string) {
    await this.page.getByLabel("Email").fill(email);
    await this.page.getByLabel("Password").fill(password);
    await this.page.getByRole("button", { name: "Sign In" }).click();
  }

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

  async expectError(message: string) {
    await expect(this.page.getByText(message)).toBeVisible();
  }
}

Verwendung in Tests:

test("successful login", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login("[email protected]", "password123");
  await loginPage.expectLoggedIn();
});

Authentifizierungstest

Gemeinsamer Authentifizierungsstatus

Vermeiden Sie die Anmeldung vor jedem Test, indem Sie den Authentifizierungsstatus speichern:

// e2e/auth.setup.ts
import { test as setup } from "@playwright/test";

setup("authenticate", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("Email").fill("[email protected]");
  await page.getByLabel("Password").fill("admin-password");
  await page.getByRole("button", { name: "Sign In" }).click();
  await page.waitForURL("/dashboard");

  // Save authentication state
  await page.context().storageState({ path: ".auth/user.json" });
});

Konfigurieren Sie in playwright.config.ts:

projects: [
  { name: "setup", testMatch: /auth\.setup\.ts/ },
  {
    name: "authenticated",
    dependencies: ["setup"],
    use: { storageState: ".auth/user.json" },
  },
],

Visueller Regressionstest

Erkennen Sie unbeabsichtigte visuelle Veränderungen mit Screenshot-Vergleichen:

test("home page visual regression", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveScreenshot("home-page.png", {
    maxDiffPixelRatio: 0.01,
  });
});

Beim ersten Start speichert Playwright grundlegende Screenshots. Nachfolgende Läufe werden mit den Basislinien verglichen und schlagen fehl, wenn die Unterschiede den Schwellenwert überschreiten. Aktualisieren Sie Baselines mit --update-snapshots, wenn Änderungen beabsichtigt sind.


API-Verspottung

Schein-API-Antworten für deterministische Tests:

test("displays products from API", async ({ page }) => {
  await page.route("/api/products", async (route) => {
    await route.fulfill({
      status: 200,
      contentType: "application/json",
      body: JSON.stringify([
        { id: 1, name: "Product A", price: 29.99 },
        { id: 2, name: "Product B", price: 49.99 },
      ]),
    });
  });

  await page.goto("/products");
  await expect(page.getByText("Product A")).toBeVisible();
  await expect(page.getByText("Product B")).toBeVisible();
});

CI/CD-Integration

GitHub-Aktionen

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
      - 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/

Berichte über fehlgeschlagene Tests werden als Artefakte zum Debuggen hochgeladen, einschließlich Screenshots, Ablaufverfolgungen und Videoaufzeichnungen.


Debugging-Tools

  • Playwright Inspector: Führen Sie Tests mit dem Flag --debug aus, um eine schrittweise Ausführung zu ermöglichen
  • Trace Viewer: Öffnen Sie Traces, um alle Aktionen, Netzwerkanfragen und DOM-Snapshots anzuzeigen
  • Codegen: Führen Sie npx playwright codegen aus, um Browserinteraktionen aufzuzeichnen und Testcode zu generieren
  • VS-Code-Erweiterung: Führen Sie Tests direkt aus VS-Code aus und debuggen Sie sie mit Click-to-Locate-Elementen

Best Practices

  1. Rollenbasierte Selektoren verwenden: Bevorzugen Sie getByRole, getByLabel, getByText gegenüber CSS-Selektoren
  2. Vermeiden Sie hartcodierte Wartezeiten: Vertrauen Sie der automatischen Wartezeit anstelle von page.waitForTimeout()
  3. Isolierte Tests: Jeder Test sollte unabhängig sein und sich nicht auf andere Tests stützen
  4. Benutzerflüsse testen, nicht Implementierung: Konzentrieren Sie sich auf das, was Benutzer tun, nicht darauf, wie der Code funktioniert
  5. Tests schnell durchführen: Verwenden Sie API-Verknüpfungen für die Einrichtung (erstellen Sie Testdaten über die API, nicht über die Benutzeroberfläche).
  6. In CI ausführen: Jede Pull-Anfrage sollte vor der Zusammenführung E2E-Tests bestehen

Häufig gestellte Fragen

F: Wie schneidet Playwright im Vergleich zu Cypress ab?

Playwright unterstützt nativ mehrere Browser (Chromium, Firefox, WebKit), während Cypress hauptsächlich auf Chromium abzielt. Playwright ist bei der parallelen Ausführung schneller und verarbeitet mehrere Registerkarten/Fenster. Cypress hat eine etwas einfachere Lernkurve und ein stärkeres Zeitreise-Debugging.

Q: How do we handle flaky tests?

Die automatische Wartefunktion von Playwright eliminiert die meisten Unzulänglichkeiten. Für verbleibende Probleme: Verwenden Sie Web-First-Assertionen (Expect Locator), vermeiden Sie das Testen von zeitabhängigem Verhalten und konfigurieren Sie Wiederholungsversuche in CI. Trace-Berichte helfen dabei, die Grundursache für zeitweilige Ausfälle zu ermitteln.

F: Sollten wir jede Seite testen?

Konzentrieren Sie sich auf kritische Benutzerströme: Authentifizierung, Kerngeschäftsabläufe, Formularübermittlungen und Zahlungsströme. Nicht jede Seite benötigt E2E-Tests – verwenden Sie Unit- und Integrationstests für eine Abdeckung auf Komponentenebene.

F: Wie lange sollten E2E-Tests dauern?

Einzelne Tests sollten in weniger als 30 Sekunden abgeschlossen sein. Eine vollständige Suite für eine mittelgroße Anwendung (50–100 Tests) sollte bei paralleler Ausführung in weniger als 10 Minuten ausgeführt werden.


Was kommt als nächstes?

E2E-Tests mit Playwright geben Ihnen die Gewissheit, dass Ihre Anwendung aus Benutzersicht korrekt funktioniert. Beginnen Sie mit kritischen Abläufen und erweitern Sie die Abdeckung, wenn Ihre Anwendung wächst.

Kontaktieren Sie ECOSIRE, um Hilfe beim Testen der Automatisierung zu erhalten, oder erkunden Sie unsere Odoo-Implementierungsdienste für eine qualitätsgesicherte ERP-Bereitstellung.


Herausgegeben von ECOSIRE – unterstützt Unternehmen bei der Skalierung mit Unternehmenssoftwarelösungen.

E

Geschrieben von

ECOSIRE Research and Development Team

Entwicklung von Enterprise-Digitalprodukten bei ECOSIRE. Einblicke in Odoo-Integrationen, E-Commerce-Automatisierung und KI-gestützte Geschäftslösungen.

Chatten Sie auf WhatsApp