Logo
  • A propos
  • Blog
  • Services
  • Media
  • Contact
Contactez-nous !

Page Object Model : La base solide pour toute automatisation UI

Date publication
Sep 2, 2025

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.

image

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

image

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) :

Avec POM (approche robuste) :

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 :

Architecture détaillée du POM

image

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.

Plus d’articles comme celui-ci

Combiner les Patterns : Architecturer un framework de test solide et évolutif
Combiner les Patterns : Architecturer un framework de test solide et évolutif
Oct 7, 2025
Screenplay Pattern : Structurer vos tests pour plus de lisibilité et de robustesse
Screenplay Pattern : Structurer vos tests pour plus de lisibilité et de robustesse
Sep 30, 2025
Builder Pattern : Créer des objets de test complexes avec clarté
Builder Pattern : Créer des objets de test complexes avec clarté
Sep 23, 2025
Facade Pattern : Cacher la complexité de vos scénarios automatisés
Facade Pattern : Cacher la complexité de vos scénarios automatisés
Sep 16, 2025
Factory Pattern : Réduire la duplication et générer vos objets de test efficacement
Factory Pattern : Réduire la duplication et générer vos objets de test efficacement
Sep 9, 2025
Page Object Model : La base solide pour toute automatisation UI
Page Object Model : La base solide pour toute automatisation UI
Sep 2, 2025
Logo

Accueil

Blog

Newsletters

Podcasts

Vidéos

Qui suis-je ?

Shift Op Solutions

Mentorat

Formations

Etat des Lieux

Contact

Copyright © Jean-François Fresi 2024 - Site créé en nocode.

LinkedInYouTubeSpotifyRSS
// 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());
}
// 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());
}
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);
    }
}