Qu’est-ce que Playwright ?
Playwright est un framework de tests de bout en bout (end-to-end) multi-navigateurs développé par Microsoft. Il pilote Chromium, Firefox et WebKit via une API unique, attend automatiquement que les éléments soient prêts et inclut un test runner avec des fixtures, du parallélisme, du tracing et un contrôle du réseau.
Il est issu de Puppeteer et en a conservé les meilleurs aspects — un protocole moderne et une API épurée — tout en ajoutant un véritable support multi-navigateurs et un outillage de premier ordre. Pour les équipes qui lancent aujourd’hui une suite de tests de bout en bout, c’est le choix par défaut le plus courant, et son mécanisme d’auto-attente (auto-waiting) est la raison principale pour laquelle ses tests sont moins instables (flaky) que ceux des outils plus anciens.
Écrire un test
Un test navigue, interagit et vérifie des assertions. Playwright fournit les fonctions test et expect ainsi qu’une fixture page.
// 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();
});
La fixture page représente un onglet de navigateur. Des actions comme click et fill attendent que l’élément soit actionnable avant d’intervenir ; il n’est donc pas nécessaire d’insérer de pauses (sleep) et vous n’aurez pas de problèmes de concurrence (race conditions).
Locators et auto-waiting
Les locators sont des requêtes “lazy” qui se résolvent en un élément lors de leur utilisation, et elles sont réessayées jusqu’à ce que l’élément soit prêt. Privilégiez les locators orientés utilisateur dans cet ordre :
- getByRole — boutons, liens, titres et champs de saisie via leur rôle accessible et leur nom.
- getByLabel — contrôles de formulaire via leur label.
- getByText — texte visible.
- getByPlaceholder, getByAltText, getByTitle — autres attributs visibles.
- getByTestId — un id de test stable lorsque rien d’orienté utilisateur ne convient.
// locators.ts
page.getByRole("link", { name: "Pricing" });
page.getByLabel("Search");
page.getByText("Welcome back");
page.getByTestId("cart-total");
Comme les actions intègrent l’auto-waiting, vous aurez rarement besoin d’attentes explicites. Les assertions sont également “web-first” : expect(locator).toBeVisible() réessaie jusqu’à ce que la condition soit remplie ou que le timeout expire, ce qui rend les tests résilients aux problèmes de timing.
// 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 et configuration partagée
Le système de fixtures de Playwright permet de partager une configuration sans avoir à la répéter. Une fixture est une valeur injectée dans les tests, créée et nettoyée par le runner.
// 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);
},
});
Pour l’authentification spécifiquement, le pattern recommandé consiste à utiliser un projet de setup qui se connecte une seule fois, sauvegarde l’état du stockage (storage state) dans un fichier, et le réutilise pour l’ensemble des tests. Cela permet de garder la suite de tests rapide et d’éviter de répéter le flux de connexion.
Contrôle du réseau
page.route intercepte les requêtes, vous permettant ainsi de tester le chargement, les erreurs et les cas limites sans avoir besoin d’un backend réel.
// 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");
Vous pouvez également vérifier le contenu envoyé par l’application, retarder les réponses pour tester les états de chargement, ou bloquer les scripts tiers pour garantir que vos tests restent rapides et déterministes.
Débogage et outillage
L’outillage de Playwright constitue une grande partie de son attrait :
- Codegen enregistre vos interactions et génère un fichier de test avec des locators.
- UI mode exécute les tests dans une interface de surveillance où vous pouvez parcourir les actions étape par étape et inspecter le DOM.
- Trace viewer enregistre une trace complète — captures d’écran, snapshots du DOM, réseau et console — que vous pouvez rejouer après un échec en CI.
- Reporters produisent nativement des rapports HTML, JUnit et d’autres formats.
Le trace viewer, en particulier, transforme un échec mystérieux en CI en un replay détaillé étape par étape, ce qui fait souvent la différence entre un correctif de cinq minutes et une après-midi passée à tâtonner.
Parallélisme et CI
Par défaut, Playwright Test exécute les fichiers de test en parallèle via des processus workers. Vous pouvez lancer la même suite de tests sur différents projets de navigateurs, ajuster le nombre de workers et répartir une suite volumineuse sur plusieurs machines avec --shard. Pour paralléliser vos tests en toute sécurité, ceux-ci doivent être indépendants et éviter tout état mutable partagé.
En CI, installez les navigateurs avec npx playwright install --with-deps, exécutez la suite de tests, puis téléchargez le rapport HTML et les traces en tant qu’artefacts. Les tentatives de relance (retries) sont disponibles pour pallier les instabilités réelles de l’infrastructure, mais elles doivent servir de filet de sécurité et non de substitut à la correction du flakiness.
Où se situe Playwright
Les tests de bout en bout (E2E) se trouvent au sommet de la pyramide : ils sont peu nombreux, lents, mais apportent une grande valeur. En dessous, on trouve les tests de composants avec Testing Library et les tests unitaires rapides avec Vitest ou Jest. Playwright couvre les parcours que seul un navigateur réel peut vérifier — l’authentification, le tunnel d’achat, la navigation et le comportement multi-navigateurs.
Bonnes pratiques
- Privilégiez les localisateurs de rôle et de label ; utilisez les test ids avec parcimonie.
- Appuyez-vous sur l’auto-waiting plutôt que sur
waitForTimeout. - Gardez vos tests indépendants pour qu’ils puissent être parallélisés en toute sécurité.
- Réutilisez l’authentification via le storage state plutôt que de vous connecter à chaque fois.
- Mockez le réseau pour les cas limites ; conservez quelques tests contre le backend réel.
- Enregistrez les traces en cas d’échec et téléchargez-les dans la CI.
- Utilisez
data-testiduniquement pour les éléments sans identité accessible.
Erreurs courantes
- Utiliser des sélecteurs CSS ou XPath liés au style et à la structure.
- Utiliser des attentes fixes avec
waitForTimeoutqui ralentissent la suite de tests et masquent les conditions de concurrence (race conditions). - Partager l’état entre les tests, ce qui casse le parallélisme.
- Tester chaque cas limite en bout-à-bout (end-to-end) au lieu de les descendre dans la pyramide des tests.
- Ignorer les traces et essayer de deviner la cause des échecs en CI.
- S’appuyer sur les tentatives de relance (retries) pour masquer une instabilité réelle (flakiness).
Et après ?
Playwright est aujourd’hui le choix par défaut le plus robuste pour les tests de bout en bout (e2e). Comparez-le avec Cypress pour découvrir une expérience développeur alternative, et construisez vos couches inférieures avec Vitest et Testing Library. Ensuite, ajoutez un premier parcours utilisateur critique comme premier test e2e, puis développez votre suite de tests progressivement.