Modèle objet de page (POM) Playwright
Ce chapitre présente la philosophie de conception et la mise en œuvre du Page Object Model pour vous aider à organiser de grandes suites de tests.
Qu'est-ce que le POM
Le Page Object Model est un patron de conception de test qui encapsule les éléments et les opérations de pagedans des classes indépendantesin.
Chaque objet de page représente une page ou un composant dans une application web et contient les localisateurs et les méthodes d'opération pour cette page.
Avantages du POM
| Avantage | Description |
|---|---|
| Simplifie l'écriture des tests | Le code de test utilise des API de haut niveau (telles queloginPage.login('user', 'pass')) au lieu d'opérations Locator brutes |
| Réduit les coûts de maintenance | Les modifications de la structure de la page ne nécessitent que la modification de la classe Page Object, et non de chaque test |
| Améliore la lisibilité | Le code de test se lit davantage comme un langage métier, ce qui le rend compréhensible même pour des personnes non techniques |
| Évite la duplication | Les mêmes localisateurs et opérations ne doivent être définis qu'une seule fois |
Création d'une classe Page Object
Ce qui suit utilise le site EXAMPLE comme exemple pour créer un Page Object pour une page de connexion.
Exemple
import { type Page, type Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
// Déclarer tous les localisateurs comme propriétés en lecture seule
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly loginButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
// Initialiser tous les localisateurs dans le constructeur
this.usernameInput = page.getByLabel('Username');
this.passwordInput = page.getByLabel('Password');
this.loginButton = page.getByRole('button', { name: 'Log in' });
this.errorMessage = page.getByTestId('login-error');
}
// Naviguer vers la page de connexion
async goto() {
await this.page.goto('/login');
}
// Effectuer l'opération de connexion
async login(username: string, password: string) {
await this.usernameInput.fill(username);
await this.passwordInput.fill(password);
await this.loginButton.click();
}
// Vérifier le message d'erreur en cas d'échec de connexion
async expectLoginError(message: string) {
await expect(this.errorMessage).toBeVisible();
await expect(this.errorMessage).toContainText(message);
}
}
Utilisation du Page Object dans les tests
Exemple
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('Fonctionnalité de connexion', () => {
test('La connexion réussit avec le nom d'utilisateur et le mot de passe corrects', async ({ page }) => {
const loginPage = new LoginPage(page);
// Utiliser les méthodes du Page Object
await loginPage.goto();
await loginPage.login('example_user', 'correct_password');
// Vérifier la redirection après une connexion réussie
await expect(page).toHaveURL('/dashboard');
});
test('La connexion échoue avec un mot de passe incorrect', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('example_user', 'wrong_password');
// Utilisez les méthodes d'assertion du Page Object
await loginPage.expectLoginError('Nom d'utilisateur ou mot de passe incorrect');
});
});
Page Objects multi-niveaux
Dans de vrais projets, vous pouvez créer plusieurs Page Objects, et ils peuvent être associés les uns aux autres.
Exemple
import { type Page, type Locator, expect } from '@playwright/test';
export class DashboardPage {
readonly page: Page;
readonly welcomeText: Locator;
readonly logoutButton: Locator;
constructor(page: Page) {
this.page = page;
this.welcomeText = page.getByText('Bienvenue à nouveau');
this.logoutButton = page.getByRole('button', { name: 'Déconnexion' });
}
async expectWelcomeMessage(username: string) {
await expect(this.welcomeText).toContainText(username);
}
async logout() {
await this.logoutButton.click();
}
}
Combinaison des objets de page :
Exemple
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);
// Connexion
await loginPage.goto();
await loginPage.login('example_user', 'password');
// Vérifier le tableau de bord
await dashboardPage.expectWelcomeMessage('example_user');
// Se déconnecter
await dashboardPage.logout();
await expect(page).toHaveURL('/login');
});
Bonnes pratiques POM
| Pratique | Description |
|---|---|
| Centraliser les localisateurs dans le constructeur | Définir tous les localisateurs dans le constructeur ; ne pas les créer dynamiquement dans les méthodes |
| Les méthodes renvoient des objets Page | Les méthodes d'opération peuvent renvoyer un nouvel objet Page (par ex., return DashboardPage après connexion) |
| Encapsuler les assertions, mais ne pas en abuser | Encapsuler les combinaisons d'assertions couramment utilisées dans des méthodes ; écrire les assertions simples directement dans les tests |
| Utiliser la composition au lieu de l'héritage | Éviter l'héritage profond ; utiliser le modèle de composition pour associer plusieurs objets Page |
| Un ensemble d'objets couvre une page/un composant | Gardez une granularité modérée ; ne créez pas de classes distinctes pour chaque petit élément |
Autres extensionsPOM n'est pas obligatoire.
Pour les petits projets avec seulement quelques tests, écrire les localisateurs directement dans les tests est plus clair. Lorsqu'il y a plus de 5 fichiers de test, envisagez d'introduire POM.