Was ist Playwright?
Playwright ist ein cross-browser end-to-end testing Framework von Microsoft. Es steuert Chromium, Firefox und WebKit über eine einzige API an, wartet automatisch, bis Elemente bereit sind, und liefert einen Test-Runner mit Fixtures, Parallelisierung, Tracing und Netzwerksteuerung mit.
Es entwickelte sich aus Puppeteer weiter und behielt die guten Aspekte bei – ein modernes Protokoll und eine saubere API –, während es echten Cross-Browser-Support und erstklassiges Tooling hinzufügte. Für Teams, die heute eine End-to-End-Suite aufsetzen, ist es die gängigste Standardwahl. Vor allem das Auto-Waiting ist der Hauptgrund dafür, dass seine Tests weniger flaky sind als bei älteren Tools.
Einen Test schreiben
Ein Test navigiert, interagiert und prüft Ergebnisse. Playwright stellt die Funktionen test und expect sowie eine page fixture bereit.
// tests/login.spec.ts
import { test, expect } from "@playwright/test";
test("user can sign in", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
Die page fixture ist ein Browser-Tab. Aktionen wie click und fill warten darauf, dass das Element ausführbar ist, bevor sie ausgeführt werden. Daher müssen keine Pausen (sleep) eingefügt werden und es gibt keine Race Conditions.
Locators und Auto-Waiting
Locators sind lazy queries, die erst bei der Verwendung in ein Element aufgelöst werden und dies so lange wiederholen, bis das Element bereit ist. Bevorzuge benutzerorientierte Locators in dieser Reihenfolge:
- getByRole — Buttons, Links, Überschriften und Inputs über die accessible role und den Namen.
- getByLabel — Formularsteuerelemente über das Label.
- getByText — sichtbarer Text.
- getByPlaceholder, getByAltText, getByTitle — andere sichtbare Attribute.
- getByTestId — eine stabile Test-ID, wenn keine benutzerorientierte Option passt.
// locators.ts
page.getByRole("link", { name: "Pricing" });
page.getByLabel("Search");
page.getByText("Welcome back");
page.getByTestId("cart-total");
Da Aktionen automatisch warten (auto-wait), benötigst du selten explizite Wartezeiten. Auch Assertions sind „web-first“: expect(locator).toBeVisible() wiederholt den Vorgang, bis die Bedingung erfüllt ist oder das Timeout abläuft, was die Tests resistent gegenüber Timing-Problemen macht.
// assertions.ts
await expect(page.getByRole("alert")).toHaveText("Saved");
await expect(page.getByRole("button", { name: "Save" })).toBeEnabled();
await expect(page.getByTestId("row")).toHaveCount(3);
Fixtures und gemeinsames Setup
Das Fixture-System von Playwright ermöglicht es, Setups zu teilen, ohne sie ständig wiederholen zu müssen. Ein Fixture ist ein Wert, der in Tests injiziert wird und vom Runner erstellt sowie wieder abgebaut wird.
// fixtures.ts
import { test as base } from "@playwright/test";
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await use(page);
},
});
Speziell für die Authentifizierung ist das empfohlene Muster ein Setup-Projekt, das sich einmal anmeldet, den Storage State in einer Datei speichert und diesen über verschiedene Tests hinweg wiederverwendet. Das hält die Testsuite schnell und vermeidet die ständige Wiederholung des Login-Flows.
Netzwerksteuerung
page.route fängt Anfragen ab, sodass Sie Ladezustände, Fehler und Edge-Cases testen können, ohne ein echtes Backend zu benötigen.
// mock.ts
await page.route("**/api/users", (route) =>
route.fulfill({
status: 500,
body: JSON.stringify({ message: "Server error" }),
}),
);
await page.goto("/users");
await expect(page.getByRole("alert")).toContainText("went wrong");
Sie können zudem prüfen, was die App gesendet hat, Antworten verzögern, um Ladezustände zu testen, oder Drittanbieter-Skripte blockieren, damit die Tests schnell und deterministisch bleiben.
Debugging und Tooling
Das Tooling von Playwright ist ein wesentlicher Teil seiner Attraktivität:
- Codegen zeichnet Ihre Interaktionen auf und generiert eine Testdatei mit den entsprechenden Locators.
- UI mode führt Tests in einer Watch-UI aus, in der Sie Aktionen Schritt für Schritt durchlaufen und den DOM inspizieren können.
- Trace viewer zeichnet einen vollständigen Trace auf – inklusive Screenshots, DOM-Snapshots, Netzwerk- und Konsolenprotokollen –, den Sie nach einem CI-Fehler erneut abspielen können.
- Reporters erstellen direkt HTML-, JUnit- und andere Berichte.
Insbesondere der Trace Viewer verwandelt mysteriöse CI-Fehler in ein Schritt-für-Schritt-Replay, was oft den Unterschied zwischen einem fünfminütigen Fix und einem ganzen Nachmittag voller Vermutungen ausmacht.
Parallelismus und CI
Playwright Test führt Testdateien standardmäßig parallel über Worker-Prozesse aus. Sie können dieselbe Suite über verschiedene Browser-Projekte laufen lassen, die Anzahl der Worker anpassen und eine große Suite mit --shard über mehrere Maschinen sharden. Um sicher zu parallelisieren, müssen Tests unabhängig sein und gemeinsam genutzte, veränderliche Zustände (shared mutable state) vermeiden.
Installieren Sie in der CI die Browser mit npx playwright install --with-deps, führen Sie die Suite aus und laden Sie den HTML-Report sowie die Traces als Artefakte hoch. Retries stehen für tatsächlich instabile Infrastrukturen zur Verfügung, sollten jedoch als Sicherheitsnetz dienen und nicht als Ersatz für die Behebung von Flakiness.
Wo Playwright ins Spiel kommt
End-to-End-Tests bilden die Spitze der Pyramide: Sie sind wenige, langsam und haben einen hohen Wert. Darunter liegen Komponententests mit Testing Library und schnelle Unit-Tests mit Vitest oder Jest. Playwright deckt die User Journeys ab, die nur ein echter Browser verifizieren kann – wie Authentifizierung, Checkout, Navigation und das Verhalten über verschiedene Browser hinweg.
Best Practices
- Bevorzugen Sie Role- und Label-Locators; verwenden Sie Test-IDs nur sparsam.
- Verlassen Sie sich auf Auto-Waiting anstatt auf
waitForTimeout. - Halten Sie Tests unabhängig, damit sie sicher parallelisiert werden können.
- Verwenden Sie den Storage State zur Wiederverwendung der Authentifizierung, anstatt sich jedes Mal neu anzumelden.
- Mocken Sie das Netzwerk für Edge Cases; behalten Sie einige Tests gegen das echte Backend bei.
- Zeichnen Sie bei Fehlern Traces auf und laden Sie diese in der CI hoch.
- Verwenden Sie
data-testidnur für Elemente ohne zugängliche Identität.
Häufige Fehler
- Verwendung von CSS- oder XPath-Selektoren, die an Styling und Struktur gebunden sind.
- Harte Wartezeiten mit
waitForTimeout, die die Testsuite verlangsamen und Race Conditions verschleiern. - Teilen von State zwischen Tests, was die Parallelisierung beeinträchtigt.
- Testen jedes Edge-Cases End-to-End, anstatt diese tiefer in der Testpyramide abzubilden.
- Ignorieren von Traces und bloßes Raten bei CI-Fehlern.
- Verlassen auf Retries, um echte Flakiness zu übertünchen.
Wie geht es weiter?
Playwright ist derzeit die beste Standardwahl für End-to-End-Tests. Vergleiche es mit Cypress, um eine alternative Developer Experience kennenzulernen, und baue die unteren Testebenen mit Vitest und Testing Library auf. Füge anschließend einen einzigen, kritischen User Journey als deinen ersten e2e-Test hinzu und erweitere deine Testsuite von dort aus.