Les entretiens Playwright testent deux choses : si vous comprenez comment Playwright fonctionne sous le capot, et si vous l'avez réellement utilisé sur des projets réels. Mémoriser des noms d'API ne suffit pas. Les recruteurs veulent voir du jugement.
Concepts fondamentaux
Qu'est-ce que Playwright et en quoi diffère-t-il de Selenium ?Playwright est un framework d'automatisation de tests web développé par Microsoft. Les différences architecturales clés avec Selenium :
Auto-waiting : Playwright attend que les éléments soient interactables avant d'agir, donc pas besoin de WebDriverWait ou d'ExpectedConditions. Selenium nécessite du code d'attente explicite.
Tout intégré : Playwright est livré avec un test runner, une bibliothèque d'assertions, un trace viewer et un reporter HTML. Selenium est une bibliothèque d'automatisation de navigateur, vous assemblez le reste vous-même.
Protocole navigateur : Playwright utilise CDP (Chrome DevTools Protocol) pour Chromium et des protocoles similaires pour Firefox et WebKit. Selenium utilise le protocole WebDriver, plus lent car il implique plus d'allers-retours réseau.
Conception language-first : l'API JavaScript/TypeScript de Playwright utilise async/await nativement. Les bindings JS de Selenium ont été ajoutés après l'implémentation Java, et ça se sent.
Avant d'exécuter une action, Playwright vérifie que l'élément cible remplit un ensemble de conditions d'"actionnabilité". Pour click() : l'élément doit être visible, dans le viewport, non couvert par un autre élément, activé et stable (pas en train de bouger à cause d'une animation). Pour fill(), l'élément doit être visible et modifiable.
Ces vérifications s'exécutent automatiquement en boucle jusqu'à ce qu'elles passent ou que le timeout soit atteint. Vous pouvez voir la vérification d'actionnabilité dans le message d'erreur quand un test échoue. Il indique exactement quelle condition n'était pas remplie.
Quelle est la différence entrepage.locator() et les méthodes getBy* ?
page.locator() prend un sélecteur CSS, XPath ou une chaîne de texte. C'est un locator générique.
Les méthodes getBy* (getByRole, getByLabel, getByText, getByTestId, etc.) sont des locators sémantiques. Ils trouvent les éléments comme un utilisateur les identifierait, en utilisant les attributs d'accessibilité plutôt que la structure CSS.
Les méthodes getBy* sont préférées car elles résistent mieux aux changements d'UI. Renommer une classe CSS ne les casse pas, mais changer le nom accessible d'un bouton si, ce qui est exactement le type de changement qui affecte les vrais utilisateurs.
Les fixtures sont le système d'injection de dépendances de Playwright. Elles fournissent des objets préconfigurés aux tests sans setup manuel dans chaque test. Les fixtures intégrées sont : page (une page de navigateur), context (un contexte de navigateur), browser (l'instance du navigateur), request (un client HTTP) et browserName.
Les fixtures personnalisées les étendent :
const test = base.extend({
loggedInPage: async ({ page }, use) => {
await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
await use(page); // passer la page connectée au test
}
});
test('le tableau de bord affiche les éléments', async ({ loggedInPage }) => {
await expect(loggedInPage.getByText('My Travel Items')).toBeVisible();
});Chaque test Playwright s'exécute dans son propre contexte de navigateur par défaut : une ardoise vierge sans cookies, sans localStorage, sans données mises en cache d'autres tests. Les tests ne s'interfèrent pas même en exécution parallèle.
Vous pouvez partager un contexte entre les tests dans un bloc test.describe avec test.describe.configure({ mode: 'serial' }), mais cela réduit l'isolation et doit être utilisé avec parcimonie.
Locators et assertions
Quand utilisergetByTestId plutôt que getByRole ?
getByRole est préféré quand l'élément a un rôle ARIA et un nom accessible significatifs. getByTestId est utile quand :
L'élément n'a pas de rôle sémantique clair (un composant personnalisé qui se rend comme un div générique).
Le nom accessible est dynamique ou non déterministe.
Les développeurs ont explicitement ajouté des attributs data-testid comme crochets de test stables.
La règle pratique : utilisez getByRole en premier, retombez sur getByTestId quand getByRole ne fonctionne pas proprement.
expect(locator).toBeVisible() ?
Il vérifie que l'élément est présent dans le DOM, n'est pas caché via CSS (display: none, visibility: hidden, opacity: 0), et a des dimensions non nulles. Un élément avec display: none échoue cette vérification. Un élément défilé hors du viewport mais non caché la passe quand même.
toBeVisible() réessaie aussi automatiquement. Il continue de vérifier jusqu'à ce que l'assertion passe ou que le timeout expire. C'est différent d'une simple vérification booléenne.
Comment asserter sur plusieurs éléments à la fois ?
// Vérifier le nombre
await expect(page.getByRole('row')).toHaveCount(6);
// Vérifier que tous les éléments d'une liste contiennent du texte
const items = page.getByRole('listitem');
await expect(items).toContainText(['Tokyo', 'Paris', 'Oslo']);
// Vérifier le texte de chaque élément
await expect(items).toHaveText(['Tokyo', 'Paris', 'Oslo']); // correspondance exactepage.waitForSelector() et une assertion de locator ?
page.waitForSelector() est une API de bas niveau qui attend que l'élément apparaisse dans le DOM. Elle est antérieure à l'API de locators de Playwright.
Les assertions de locators comme await expect(locator).toBeVisible() sont l'approche moderne. Elles sont plus expressives, réessaient automatiquement et fonctionnent de façon cohérente avec le système de locators de Playwright. Utilisez les assertions dans les tests, pas waitForSelector.
Réseau et API
Comment intercepter une requête réseau dans Playwright ?// Intercepter et modifier une réponse
await page.route('**/api/items', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, destination: 'Ville Mockée' }])
});
});
// Bloquer des requêtes spécifiques
await page.route('**/*.png', route => route.abort());
// Modifier les en-têtes de requête
await page.route('**/api/**', route => {
route.continue({
headers: { ...route.request().headers(), 'X-Custom-Header': 'test' }
});
});page.request et la fixture request ?
page.request partage le contexte du navigateur. Il envoie des requêtes avec les mêmes cookies et l'état d'authentification de la page de navigateur courante.
La fixture request est un client HTTP autonome sans session de navigateur. Utilisez request pour les tests d'API purs qui n'ont pas besoin de l'état du navigateur. Utilisez page.request quand vous devez faire des appels API avec la session de l'utilisateur actuellement connecté.
// Attendre une requête et réponse spécifiques
const responsePromise = page.waitForResponse('**/api/items');
await page.getByRole('button', { name: 'Actualiser' }).click();
const response = await responsePromise;
expect(response.status()).toBe(200);Configuration et CI
Comment lancer les tests Playwright dans GitHub Actions ?# .github/workflows/playwright.yml
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/Points clés : npm ci plutôt que npm install pour la reproductibilité. npx playwright install --with-deps installe les navigateurs et leurs dépendances système. if: always() sur l'upload du rapport garantit que vous obtenez le rapport même quand les tests échouent.
Le sharding divise votre suite de tests entre plusieurs machines. Chaque shard exécute un sous-ensemble des tests :
npx playwright test --shard=1/4 # cette machine exécute 25% des tests
npx playwright test --shard=2/4
npx playwright test --shard=3/4
npx playwright test --shard=4/4Utilisez-le quand votre suite prend plus de 5 minutes sur une seule machine et que vous voulez un retour CI plus rapide. Les tests doivent être isolés (pas d'état partagé) pour que le sharding fonctionne correctement.
Que faittrace: 'on-first-retry' dans playwright.config.ts ?
Quand un test échoue et est relancé, Playwright enregistre une trace. C'est un enregistrement complet de l'exécution du test incluant les snapshots DOM, les requêtes réseau, les captures d'écran à chaque étape et les logs console. La trace est sauvegardée dans le dossier des résultats et consultable avec npx playwright show-report ou en téléchargeant sur trace.playwright.dev.
Architecture et schémas
Quand utiliser le Page Object Model plutôt que des tests simples ?Utilisez des tests simples quand : vous avez peu de tests (moins de 20), les tests sont exploratoires, ou vous débutez.
Utilisez le POM quand : plusieurs tests interagissent avec la même page, des locators sont répétés dans plusieurs fichiers de test, ou l'UI change fréquemment et vous voulez mettre à jour les locators à un seul endroit.
Le déclencheur pratique : quand vous vous retrouvez à copier-coller les mêmes 5+ lignes entre les tests, il est temps d'un page object.
Comment gérer l'authentification entre les tests sans se connecter pour chaque test ?storageState sauvegarde et restaure les cookies et le localStorage du navigateur. Lancez la connexion une fois dans un fichier de global setup, sauvegardez l'état et configurez les tests pour le charger :
// global-setup.ts
import { chromium } from '@playwright/test';
export default async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
await page.context().storageState({ path: 'auth.json' });
await browser.close();
}// playwright.config.ts
export default defineConfig({
globalSetup: './global-setup.ts',
use: { storageState: 'auth.json' }
});Dans l'ordre : les variables d'environnement (secrets manquants en CI), l'URL de base (localhost codé en dur) et le timing (les runners CI sont plus lents, ajoutez retries: 1). Vérifiez aussi un await manquant et les différences de version de navigateur. Téléchargez l'artefact de trace CI et comparez l'exécution pas à pas avec la trace locale.
PWDEBUG=1 npx playwright test ouvre l'inspecteur avec exécution pas à pas. Utilisez-le pour comprendre ce que Playwright fait réellement avant de supposer qu'il y a un bug.FAQ
Dois-je connaître les internals de Playwright pour un entretien senior ?Comprendre le protocole CDP, comment l'auto-waiting est implémenté et comment fonctionnent les fixtures sous le capot montre de la profondeur. On ne vous demandera pas de les implémenter, mais expliquer "l'auto-waiting sonde les vérifications d'actionnabilité sur un timer" est plus impressionnant que "ça attend automatiquement".
Comment démontrer mon expérience Playwright si je ne l'ai pas utilisé professionnellement ?Un dépôt GitHub avec 20+ tests couvrant la connexion d'une vraie application, des opérations CRUD et des appels API est une preuve concrète. lab.becomeqa.com est spécifiquement conçu pour ça. Guidez le recruteur à travers votre structure de tests et expliquez pourquoi vous avez fait les choix architecturaux que vous avez faits.