Page Object Model : La base solide pour toute automatisation UI
Cet article est le premier d’une série dédiée aux design patterns essentiels pour l'automatisation des tests.
Nous explorerons ensemble les fondations d'une architecture de test robuste et maintenable à partir d’autress patterns incontournables : Factory, Facade, Screenplay… jusqu’à l’art de les combiner pour construire un framework de test maintenable et évolutif.
Un peu d’histoire : Un pattern né de la nécessité
Dans l'univers de l'automatisation des tests UI, il existe un pattern qui fait l'unanimité chez les professionnels de la qualité logicielle : le Page Object Model (POM). Né de la frustration des équipes face à des suites de tests fragiles et difficiles à maintenir, ce pattern a révolutionné notre approche de l'automatisation.
L'histoire du POM remonte aux premiers jours de Selenium, quand les développeurs ont réalisé que leurs tests étaient trop couplés à l'interface utilisateur. Un simple changement d'ID ou de classe CSS pouvait briser des dizaines de tests. C'est ainsi qu'est né ce pattern élégant, transformant le chaos en ordre et la fragilité en robustesse.
C’est pour répondre à ce problème que le pattern POM a été formalisé : isoler la logique des pages dans des classes dédiées et offrir aux tests une interface claire, simple et réutilisable
Architecture du Pattern
Le Page Object Model consiste à représenter chaque page (ou composant d’interface) d’une application comme un objet.
- Les sélecteurs sont centralisés dans cet objet.
- Les méthodes encapsulent les actions possibles (cliquer, saisir un texte, vérifier un élément).
- Les tests consomment ces méthodes sans jamais manipuler directement les sélecteurs.
L’objectif est de séparer la logique métier (les tests) de la logique technique (les pages), pour un code plus robuste et maintenable. Cette séparation claire permet d'isoler chaque responsabilité et de créer une architecture modulaire.
Exemple concret : Automatiser un login e-commerce
Prenons l'exemple d'un site e-commerce typique avec une page de connexion. Voici comment structurer notre automatisation avec le POM :
Sans POM (approche fragile) :
// Test fragile et difficile à maintenir
@Test
public void testLogin() {
driver.findElement(By.id("email")).sendKeys("user@example.com");
driver.findElement(By.id("password")).sendKeys("password123");
driver.findElement(By.xpath("//button[@class='login-btn primary']")).click();
WebElement welcomeMsg = driver.findElement(By.className("welcome-message"));
Assert.assertTrue(welcomeMsg.isDisplayed());
}
Avec POM (approche robuste) :
// Page Object pour la page de connexion
public class LoginPage {
private WebDriver driver;
// Éléments de la page
@FindBy(id = "email")
private WebElement emailField;
@FindBy(id = "password")
private WebElement passwordField;
@FindBy(xpath = "//button[@class='login-btn primary']")
private WebElement loginButton;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
// Actions métier
public HomePage login(String email, String password) {
emailField.sendKeys(email);
passwordField.sendKeys(password);
loginButton.click();
return new HomePage(driver);
}
public boolean isEmailFieldVisible() {
return emailField.isDisplayed();
}
}
// Test utilisant le Page Object
@Test
public void testSuccessfulLogin() {
LoginPage loginPage = new LoginPage(driver);
HomePage homePage = loginPage.login("user@example.com", "password123");
Assert.assertTrue(homePage.isWelcomeMessageDisplayed());
}
Page Component Objects : Penser composant
Comme le souligne Martin Fowler dans sa définition du Page Object, le pattern peut également s'appliquer au niveau des composants. Cette approche, appelée Page Component Objects, est particulièrement utile pour les applications modernes avec des composants réutilisables.
La documentation officielle de Selenium recommande cette approche pour gérer des éléments comme :
- Headers de navigation
- Formulaires complexes
- Modales et popups
- Carousels et widgets
Exemple de Component Object :
public class NavigationComponent {
@FindBy(css = ".nav-search")
private WebElement searchBox;
@FindBy(css = ".nav-cart")
private WebElement cartIcon;
@FindBy(css = ".nav-profile")
private WebElement profileMenu;
public SearchResultsPage search(String query) {
searchBox.sendKeys(query);
searchBox.submit();
return new SearchResultsPage(driver);
}
public CartPage goToCart() {
cartIcon.click();
return new CartPage(driver);
}
}
Architecture détaillée du POM
Bonnes pratiques essentielles
1. Pas de logique métier dans les Page Objects
Les Page Objects ne doivent contenir que des actions techniques, pas de logique métier complexe :
// ❌ Mauvais : logique métier dans le Page Object
public boolean loginWithValidCredentials() {
if (isUserAlreadyLoggedIn()) {
return true;
}
// Logique complexe...
}
// ✅ Bon : actions simples et techniques
public HomePage login(String email, String password) {
emailField.sendKeys(email);
passwordField.sendKeys(password);
loginButton.click();
return new HomePage(driver);
}
2. Centralisation des sélecteurs
Tous les sélecteurs doivent être définis au même endroit :
public class LoginPage {
// ✅ Centralisation des sélecteurs
private static final By EMAIL_FIELD = By.id("email");
private static final By PASSWORD_FIELD = By.id("password");
private static final By LOGIN_BUTTON = By.css(".login-btn");
}
3. Méthodes fluides pour l'enchaînement
Retournez des instances de Page Objects pour permettre l'enchaînement :
// ✅ API fluide
LoginPage loginPage = new LoginPage(driver);
HomePage homePage = loginPage
.enterEmail("user@test.com")
.enterPassword("password")
.clickLogin();
4. Nommage expressif
Utilisez des noms de méthodes qui reflètent l'action métier :
// ✅ Nommage expressif
public HomePage performSuccessfulLogin(String email, String password)
public void displayErrorMessage()
public boolean isLoginFormVisible()
FAQ
Q: Faut-il créer un Page Object pour chaque page de l'application ?
R: Pas nécessairement. Créez des Page Objects pour les pages que vous testez activement. Pour les pages de passage ou très simples, ce n'est pas toujours justifié.
Q: Comment gérer les éléments dynamiques avec le POM ?
R: Utilisez des attentes explicites (WebDriverWait) dans vos méthodes de Page Object et paramétrez vos sélecteurs quand nécessaire.
Q: Le POM est-il adapté aux applications SPA (Single Page Application) ?
R: Oui, mais adaptez votre approche. Pensez en termes de "vues" ou "composants" plutôt qu'en "pages" traditionnelles.
Q: Comment tester les Page Objects eux-mêmes ?
R: Les Page Objects sont du code de production pour vos tests. Ils doivent être simples et ne pas nécessiter de tests unitaires spécifiques. La validation se fait à travers les tests fonctionnels.
Q: Peut-on combiner POM avec d'autres patterns ?
R: Absolument ! Le POM se marie parfaitement avec des patterns comme Factory, Builder ou Screenplay que nous explorerons dans les prochains articles.
Q: Est-ce que le POM rend mon framework plus lent ?
R: Non. Le POM n’est pas une librairie, mais une organisation de code. Il n’impacte pas les performances.
Q: Dois-je créer un Page Object par page obligatoirement ?
R: Pas toujours. Parfois, un composant suffit. L’idée est d’éviter la duplication et de rendre le code clair, pas de créer des classes inutiles.
A retenir
Le Page Object Model est la pierre angulaire de toute stratégie d’automatisation UI.
Il apporte clarté, maintenabilité et robustesse, en séparant l’intention métier du détail technique.
Dans le prochain article, nous découvrirons le Factory Pattern et comment il peut réduire la duplication dans vos suites de tests en générant efficacement vos objets de test.