Playwright Netzwerk-Interception und Route-Mocking im Detail
AI generated
PASS
expect()
Playwright · Netzwerk-Mocking
Playwright Netzwerk-Interception und Route-Mocking
Wie page.route() Netzwerkantworten gezielt abfängt, ersetzt und damit E2E-Tests schneller und zuverlässiger macht

Playwrights page.route()-API erlaubt es, jede einzelne Netzwerkanfrage einer Seite abzufangen, bevor sie tatsächlich an den Server geht, und wahlweise mit einer eigenen, kontrollierten Antwort zu beantworten, die tatsächliche Anfrage unverändert durchzulassen, oder sie vollständig zu blockieren. Diese Fähigkeit macht Route-Mocking zu einem der wirkungsvollsten Werkzeuge gegen die häufigste Ursache instabiler E2E-Tests: die Abhängigkeit von einem echten, möglicherweise langsamen oder inkonsistenten Backend-System.

15 Min. Lesezeit Playwright Netzwerk-Mocking

1. Warum echte Backend-Aufrufe E2E-Tests instabil machen

Ein E2E-Test, der bei jedem Lauf tatsächlich gegen ein echtes Backend-System testet, übernimmt automatisch dessen gesamte Instabilität: Ein langsamer Datenbank-Query verzögert den Test, ein kurzzeitig überlasteter Drittanbieter-Dienst lässt den Test fehlschlagen, und sich ständig ändernde Produktivdaten machen Assertions auf konkrete Inhalte unzuverlässig. Diese externen Faktoren haben nichts mit der eigentlichen Qualität des zu testenden Frontend-Codes zu tun, führen aber trotzdem regelmäßig zu fehlgeschlagenen Testläufen, die Entwicklerinnen und Entwickler mit der Zeit dazu verleiten, rote Tests reflexartig zu ignorieren statt sie ernst zu nehmen.

Route-Mocking löst dieses Problem, indem es die Frontend-Tests von der tatsächlichen Backend-Verfügbarkeit entkoppelt: Der Test kontrolliert exakt, welche Antwort eine bestimmte Netzwerkanfrage erhält, unabhängig davon, ob das echte Backend gerade erreichbar, langsam oder inhaltlich verändert ist, wodurch der Test ausschliesslich das tatsächliche Frontend-Verhalten prüft, nicht die Zuverlässigkeit der gesamten Infrastruktur. Diese Entkopplung zahlt sich besonders in größeren Teams aus, in denen Frontend- und Backend-Entwicklung parallel und unabhängig voneinander voranschreiten, da Frontend-Tests dadurch nicht länger auf einen stets funktionierenden, aktuellen Backend-Stand angewiesen sind.

Ein weiterer, oft unterschätzter Vorteil ist die deutlich höhere Ausführungsgeschwindigkeit gemockter Tests gegenüber Tests mit echten Netzwerkaufrufen, da eine lokal beantwortete, gemockte Anfrage in der Regel binnen Millisekunden zurückkommt, während ein echter Datenbank-Roundtrip mit Netzwerklatenz, Anwendungslogik und Serialisierung leicht das Zehn- bis Hundertfache an Zeit benötigt, was sich bei hunderten Tests in einer CI-Pipeline zu einer erheblichen Gesamtlaufzeitersparnis summiert.

2. Grundlegende Nutzung von page.route()

Der grundlegende Aufbau von page.route() besteht aus einem URL-Muster, das bestimmt, welche Anfragen abgefangen werden sollen, und einem Handler, der entscheidet, wie mit der abgefangenen Anfrage verfahren wird, etwa durch Beantworten mit einer eigenen, festgelegten JSON-Antwort statt die Anfrage tatsächlich an den Server weiterzuleiten.


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

test('zeigt Warenkorb-Anzahl aus gemocktem Backend', async ({ page }) => {
  await page.route('**/rest/V1/carts/mine', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ items_count: 3, items_qty: 5 }),
    });
  });

  await page.goto('/checkout/cart');
  await expect(page.locator('[data-testid="cart-count"]')).toHaveText('3');
});

3. Fehlerzustände und langsame Antworten gezielt simulieren

Ein besonders wertvoller Anwendungsfall von Route-Mocking ist das gezielte Simulieren von Fehlerzuständen, die im echten Backend nur schwer oder gar nicht reproduzierbar wären, etwa ein 500er-Serverfehler, eine 429-Rate-Limit-Antwort, oder eine Anfrage, die niemals antwortet und dadurch das Timeout-Verhalten des Frontends prüft. Ohne Route-Mocking müsste ein Entwicklungsteam entweder das echte Backend künstlich in einen Fehlerzustand versetzen, was riskant und aufwendig ist, oder auf das Testen dieser wichtigen Fehlerpfade komplett verzichten.

Für das gezielte Testen von Ladezuständen und Race Conditions im Frontend lässt sich eine künstliche Verzögerung in den Route-Handler einbauen, die vor der eigentlichen Antwort wartet, wodurch Assertions auf Lade-Spinner oder Skeleton-Screens zuverlässig und reproduzierbar geprüft werden können, statt auf zufällig auftretende Verzögerungen im echten Backend angewiesen zu sein.


test('zeigt Fehlermeldung bei Serverfehler', async ({ page }) => {
  await page.route('**/rest/V1/carts/mine', (route) =>
    route.fulfill({ status: 500, body: JSON.stringify({ message: 'Serverfehler' }) })
  );

  await page.goto('/checkout/cart');
  await expect(page.locator('[data-testid="error-banner"]')).toBeVisible();
});

test('zeigt Lade-Spinner während langsamer Antwort', async ({ page }) => {
  await page.route('**/rest/V1/carts/mine', async (route) => {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    await route.continue();
  });

  await page.goto('/checkout/cart');
  await expect(page.locator('[data-testid="loading-spinner"]')).toBeVisible();
});

4. Teilweises Mocking: nur bestimmte Anfragen abfangen

In vielen Testszenarien ist es sinnvoller, nur eine einzelne, für den Test relevante Anfrage zu mocken und alle anderen Anfragen unverändert an das echte Backend durchzulassen, statt die gesamte Seite vollständig zu isolieren. Dieses gezielte, teilweise Mocking lässt sich erreichen, indem der Handler für nicht-relevante URL-Muster `route.continue()` statt `route.fulfill()` aufruft, wodurch die Anfrage unverändert weitergeleitet wird.

Dieser hybride Ansatz eignet sich besonders für Tests, die einen realistischen End-to-End-Fluss abbilden sollen, aber gezielt einen einzelnen, schwer reproduzierbaren Zustand (etwa "Lagerbestand auf null") simulieren müssen, ohne dafür die gesamte Testumgebung künstlich vorzubereiten.

5. Unterschiede zu Cypress cy.intercept()

Cypress bietet mit cy.intercept() eine konzeptionell ähnliche Fähigkeit zum Abfangen und Mocken von Netzwerkanfragen, unterscheidet sich aber in einem wichtigen architektonischen Punkt: Cypress läuft im selben Browser-Prozess wie die getestete Anwendung und fängt Anfragen auf Service-Worker-Ebene ab, während Playwright das Abfangen über das Chrome DevTools Protocol beziehungsweise die jeweiligen Browser-Debugging-Schnittstellen realisiert, was Playwright vollständige Kontrolle über alle drei unterstützten Browser-Engines (Chromium, Firefox, WebKit) mit identischer API gibt.

In der praktischen Anwendung führt dieser Unterschied dazu, dass Playwrights Route-Mocking tendenziell konsistenter über verschiedene Browser hinweg funktioniert, während Cypress historisch stärker auf Chromium-basierte Browser optimiert war, ein Aspekt, der bei einer bewussten Werkzeugentscheidung zwischen beiden Frameworks durchaus mitentscheidend sein kann, insbesondere wenn Cross-Browser-Kompatibilität für die eigene Nutzerbasis eine tatsächlich hohe Priorität hat und nicht nur eine theoretische Anforderung darstellt.

6. Häufige Fallstricke beim Route-Mocking

Ein häufiger Fehler ist ein zu allgemeines URL-Muster, das versehentlich mehr Anfragen abfängt als beabsichtigt, etwa `**/api/**` statt eines spezifischeren Musters, wodurch auch für den Test irrelevante, aber notwendige Anfragen (etwa das Laden von Bildern oder Fonts) unerwartet blockiert oder verfälscht werden und der Test aus einem ganz anderen Grund fehlschlägt als eigentlich beabsichtigt.

Ein weiterer verbreiteter Fallstrick ist, gemockte Antworten von der tatsächlichen API-Vertragsform der echten Anwendung abweichen zu lassen, etwa durch veraltete oder unvollständige Beispiel-Payloads, wodurch ein Test zwar grün bleibt, obwohl sich das echte Backend-Schema längst geändert hat. Ein regelmäßiger Abgleich der gemockten Antwortstrukturen gegen echte, aktuelle API-Antworten, etwa über Contract-Testing (siehe den separaten Artikel zu Contract Testing mit Pact), reduziert dieses Risiko erheblich. Ein drittes, weniger offensichtliches Problem entsteht, wenn Teammitglieder unabhängig voneinander leicht unterschiedliche Mock-Antworten für denselben Endpunkt schreiben, wodurch Tests je nach Autor unterschiedliche, teils widersprüchliche Annahmen über dasselbe Backend-Verhalten treffen und sich Fehler dadurch schwerer nachvollziehen lassen.

7. Wann auf Route-Mocking bewusst verzichtet werden sollte

Nicht jeder Test sollte gemockt werden: Für den kritischsten, zentralen Nutzerpfad einer Anwendung, etwa den vollständigen Checkout-Prozess von Magento, ist ein zumindest gelegentlicher, echter End-to-End-Test gegen eine realistische Staging-Umgebung wertvoll, um tatsächliche Integrationsprobleme zwischen Frontend und Backend aufzudecken, die reines Mocking naturgemäß nicht erkennen kann.

Eine bewährte Praxis ist deshalb eine Mischstrategie: Die große Mehrheit der Tests nutzt Route-Mocking für Geschwindigkeit und Stabilität, während eine kleine Anzahl kritischer Smoke-Tests regelmäßig, aber seltener, tatsächlich ungemockt gegen eine echte Umgebung läuft, um die durch Mocking entstehende "Integrationslücke" gezielt abzudecken. Diese wenigen, ungemockten Tests laufen dabei sinnvollerweise nicht bei jedem einzelnen Commit, sondern etwa täglich oder vor jedem Produktiv-Deployment, um die zusätzliche Laufzeit und Instabilität in einem vertretbaren Rahmen zu halten.

8. HAR-Dateien für realistische, aufgezeichnete Antworten nutzen

Statt jede gemockte Antwort manuell als Inline-JSON zu schreiben, unterstützt Playwright das Aufzeichnen echter Netzwerkverkehr-Sitzungen als HAR-Datei (HTTP Archive Format), die anschliessend als Grundlage für realistische, aus echten Anfragen stammende Mock-Antworten dient, statt handgeschriebene, möglicherweise unvollständige Beispieldaten zu verwenden.

Dieser Ansatz kombiniert die Stabilitätsvorteile von Route-Mocking mit der Realitätsnähe echter Backend-Antworten und eignet sich besonders gut für komplexe, schwer von Hand nachzubildende API-Antworten, etwa tief verschachtelte Produktkatalog-Strukturen mit vielen Varianten und Attributen. Ein praktischer Zwischenweg besteht darin, eine HAR-Aufzeichnung einmalig aus einer echten Staging-Umgebung zu erstellen, sie anschliessend versioniert im Testrepository abzulegen und bei Bedarf gezielt einzelne Werte darin zu überschreiben, statt bei jeder kleinen Testfall-Variation eine komplett neue Aufzeichnung anzufertigen.

9. Mocking-Strategien im Überblick

Die folgende Tabelle vergleicht die vorgestellten Ansätze für Netzwerk-Interception.

Ansatz Geeignet für Nachteil
Inline-JSON-Mock Einfache, feste Testfälle Manuelle Pflege bei API-Änderungen
Teilweises Mocking Realistische Flüsse mit einem Sonderfall Weiterhin Backend-Abhängigkeit
HAR-Aufzeichnung Komplexe, realistische Antworten Muss bei API-Änderungen neu aufgezeichnet werden
Kein Mocking (echtes Backend) Kritische Smoke-Tests Langsam, instabil bei Backend-Problemen

Mironsoft

E2E-Teststrategie, CI-Integration und stabile Testsuiten

Testsuiten, die Bugs finden statt nur rot zu blinken?

Wir prüfen bestehende E2E-Testsuiten auf Flakiness, fehlende Testisolation und ineffiziente CI-Laufzeiten und bauen daraus eine Teststrategie, die tatsächlich Vertrauen schafft statt nur Haken zu setzen.

Test-Audit

Flaky Tests, Testpyramide und Coverage-Lücken systematisch aufdecken.

CI-Optimierung

Parallele Ausführung, Retry-Strategien und schnelle Feedback-Zyklen aufbauen.

Cypress/Playwright-Setup

Robuste E2E-Suiten für Magento-Frontends von Grund auf einrichten.

10. Zusammenfassung

Route-Mocking: Das Wichtigste auf einen Blick

Kernidee

page.route() fängt Netzwerkanfragen ab, bevor sie den Server erreichen, und ersetzt sie durch kontrollierte Antworten.

Stärke

Fehlerzustände, Ladezeiten und Race Conditions lassen sich gezielt und reproduzierbar simulieren.

Fallstrick

Zu allgemeine URL-Muster und veraltete Mock-Antworten sind die häufigsten Fehlerquellen.

Balance

Mischstrategie aus gemockten Tests für Geschwindigkeit und wenigen echten Smoke-Tests für Integrationssicherheit.

11. FAQ: Route-Mocking: Das Wichtigste auf einen Blick

1Ersetzt Route-Mocking komplett echte Backend-Tests?
Nein, eine kleine Anzahl kritischer Smoke-Tests sollte weiterhin gegen eine echte Umgebung laufen.
2Ist page.route() nur für JSON-APIs geeignet?
Nein, es funktioniert für jede Art von Netzwerkanfrage, einschliesslich HTML, Bilder und andere Ressourcentypen.
3Wie unterscheidet sich Playwright von Cypress beim Mocking?
Playwright nutzt das Chrome DevTools Protocol browserübergreifend, Cypress arbeitet auf Service-Worker-Ebene primär in Chromium.
4Was ist der Vorteil von HAR-Dateien gegenüber Inline-Mocks?
Sie basieren auf tatsächlich aufgezeichnetem Netzwerkverkehr und sind dadurch realistischer als handgeschriebene Beispieldaten.
5Kann ich nur eine einzelne Anfrage mocken und den Rest normal laden lassen?
Ja, über route.continue() für nicht-relevante Muster, kombiniert mit route.fulfill() für die gezielt gemockte Anfrage.
6Wie simuliere ich einen Timeout im Frontend-Test?
Durch einen Route-Handler, der niemals antwortet oder eine sehr lange Verzögerung einbaut.
7Ist Route-Mocking auch für GraphQL-APIs nutzbar?
Ja, da GraphQL meist über einen einzelnen POST-Endpunkt läuft, lässt sich dieser genauso abfangen wie eine REST-Anfrage.
8Verlangsamt Route-Mocking die Testausführung?
Im Gegenteil, gemockte Antworten kommen meist schneller zurück als echte Backend-Aufrufe, was Tests beschleunigt.
9Wie vermeide ich veraltete Mock-Antworten?
Durch regelmäßigen Abgleich gegen echte API-Antworten, idealerweise ergänzt durch Contract-Testing.
10Sollte jedes Team-Mitglied eigene Mock-Daten schreiben?
Besser: eine gemeinsame, versionierte Sammlung von Mock-Fixtures pflegen, um Inkonsistenzen zu vermeiden.