Structure des tests et hooks avec Playwright
Ce chapitre présente comment les tests Playwright sont organisés, y compris le regroupement des tests, le contrôle d'exécution et l'utilisation des hooks de cycle de vie.
test.describe() Regroupement des tests
test.describe()Utilisé pour organiser les tests associés en groupes logiques.
Exemple
import { test, expect } from '@playwright/test';
// Regroupement de premier niveau
test.describe('Test de la page d'accueil EXAMPLE', () => {
test('Le titre de la page est correct', async ({ page }) => {
await page.goto('https://www.example.com/');
await expect(page).toHaveTitle(/EXAMPLE/);
});
test('La barre de navigation existe', async ({ page }) => {
await page.goto('https://www.example.com/');
// Vérifier que l'élément de navigation existe
await expect(page.locator('nav')).toBeVisible();
});
});
// Plusieurs blocs describe peuvent être définis
test.describe('Test de la fonctionnalité de recherche', () => {
test('La zone de recherche est visible', async ({ page }) => {
await page.goto('https://www.example.com/');
await expect(
page.getByPlaceholder('Rechercher')
).toBeVisible();
});
});
Le rapport de test seradescribeaffiché en groupes, ce qui facilite la consultation des résultats.
describe imbriqué
test.describe()Ils peuvent être imbriqués pour former une structure arborescente :
Exemple
test.describe('Fonctionnalité de connexion', () => {
test('Connexion avec un compte et un mot de passe corrects', async ({ page }) => { /* ... */ });
test('La connexion échoue avec un mot de passe incorrect', async ({ page }) => { /* ... */ });
});
test.describe('Fonctionnalité d’inscription', () => {
test('S’inscrire avec des informations correctes', async ({ page }) => { /* ... */ });
test('L’inscription échoue avec un e-mail existant', async ({ page }) => { /* ... */ });
});
});
test.skip() Ignorer les tests
test.skip()Utilisé pour ignorer un test ; les tests ignorés ne seront pas exécutés.
Exemple
test.skip('Test de fonctionnalité pas encore terminé', async ({ page }) => {
// Ce test ne sera pas exécuté
});
// Ignorer conditionnellement (déterminé à l’exécution)
test('Ignorer sous certaines conditions', async ({ page, browserName }) => {
// Ignorer dans Firefox
test.skip(browserName === 'firefox', 'Firefox ne prend pas encore en charge cette fonctionnalité');
// La logique du test ne s'exécute que dans les navigateurs non Firefox
await page.goto('https://www.example.com/');
});
test.fail() Marquer un échec attendu
test.fail()Utilisé pour marquer un test connu pour échouer.
Exemple
test.fail('Bug connu : la fonctionnalité de recherche n'est pas encore implémentée', async ({ page }) => {
await page.goto('https://www.example.com/');
// Cette assertion est censée échouer
await expect(page.locator('.search-results')).toBeVisible();
});
// Si le test réussit de manière inattendue, il sera signalé comme un échec
// Ceci vous rappelle : "Le bug est corrigé, vous pouvez maintenant retirer le marqueur test.fail"
test.fail()Son objectif est un marquage inversé : échec du test = comme prévu, réussite du test = anormal (le marqueur devra peut-être être retiré).
test.only() Exécuter uniquement le test actuel
test.only()Utilisé pour exécuter uniquement des tests spécifiques pendant le développement et le débogage, en ignorant les autres tests du fichier.
Exemple
test.only('Je débogue ce test', async ({ page }) => {
// Seul ce test sera exécuté
await page.goto('https://www.example.com/');
});
test('Ce test sera ignoré', async ({ page }) => {
// À cause de test.only() ci-dessus, ceci ne sera pas exécuté
});
test.only()est un outil de débogage et ne doit pas être commité dans le dépôt de code. Si vousplaywright.config.tsdéfinissez ... dansforbidOnly: true, CI détecteraonlyet fera échouer le build.
test.fixme() Marquer comme à corriger
test.fixme()ettest.skip()Similaire, mais sémantiquement cela signifie « ce test doit être corrigé. »
Exemple
test.fixme('Le flux de connexion doit être mis à jour en raison de modifications de l’API', async ({ page }) => {
// Ce test ne s'exécutera pas pour l'instant
});
test.slow() Marquer les tests comme lents
test.slow()Multiplie par 3 le délai d'attente du test.
Exemple
test.slow('Test sur un grand volume de données', async ({ page }) => {
// Le délai d'attente par défaut pour ce test sera de 90 secondes (30s × 3)
// Convient aux scénarios nécessitant un chargement important de données ou des calculs complexes
});
Hooks de test
Les fonctions de hook vous permettent d’effectuer des tâches de préparation et de nettoyage à différentes étapes des tests.
test.beforeEach() et test.afterEach()
beforeEachS'exécute avant chaque test,afterEachet s'exécute après chaque test.
Exemple
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// Naviguer vers la même page avant chaque test
await page.goto('https://www.example.com/');
});
test.afterEach(async ({ page }) => {
// Effectuer le nettoyage après chaque test (par exemple, supprimer les données créées)
// Playwright nettoie automatiquement les contextes de navigateur,
// Le nettoyage ici concerne principalement l'état côté serveur
console.log('Test terminé, démarrage du nettoyage...');
});
test('Premier test', async ({ page }) => {
// la page est déjà sur la page d'accueil EXAMPLE
await expect(page).toHaveTitle(/EXAMPLE/);
});
test('Deuxième test', async ({ page }) => {
// la page est aussi déjà sur la page d'accueil EXAMPLE
// Il s'agit d'un tout nouveau contexte ; les cookies/stockage du test précédent ne persisteront pas
await expect(page.locator('nav')).toBeVisible();
});
test.beforeAll() et test.afterAll()
beforeAllDansDans la portée actuelle,s'exécute une fois avant tous les tests,afterAllet s'exécute une fois après tous les tests.
Exemple
test.beforeAll(async ({ browser }) => {
// Créer un utilisateur de test avant tous les tests (exécuté une seule fois)
// Remarque : beforeAll ne peut pas utiliser la fixture page
});
test.afterAll(async ({ browser }) => {
// Supprimer l'utilisateur de test après tous les tests (exécuté une seule fois)
});
test('Voir la liste des utilisateurs', async ({ page }) => { /* ... */ });
test('Modifier les informations de l'utilisateur', async ({ page }) => { /* ... */ });
});
beforeAlletafterAllimpossible d'accéderpagela fixture, car page est indépendante pour chaque test. Si vous devez intervenir sur une page, utilisezbeforeEach。
Portée des hooks et ordre d'exécution
La portée des hooks dépend de l'endroit où ils sont définis :
Exemple
test.beforeEach(async () => {
console.log('beforeEach au niveau du fichier');
});
test.describe('Groupe A', () => {
// Hook au niveau du groupe — s'applique uniquement au groupe A
test.beforeEach(async () => {
console.log("beforeEach du groupe A");
});
test('Test A1', async () => { /* ... */ });
test('Test A2', async () => { /* ... */ });
});
test.describe('Groupe B', () => {
// Le groupe B n'a pas de hooks propres ; seul le hook au niveau du fichier prend effet
test('Test B1', async () => { /* ... */ });
});
Ordre d'exécution : les hooks externes s'exécutent en premier, les hooks internes s'exécutent ensuite.
Pour A1 et A2 : première sortie"文件级 beforeEach", puis la sortie"分组 A 的 beforeEach"。
Pour B1 : uniquement la sortie"文件级 beforeEach"。