Combiner les Patterns : Architecturer un framework de test solide et évolutif
Cet article est le dernier de notre série sur les design patterns appliqués à l’automatisation des tests. Après avoir exploré individuellement le Page Object Model, le Factory Pattern, le Facade Pattern, le Builder Pattern et le Screenplay Pattern, il est temps de découvrir comment les combiner intelligemment pour créer un framework de test robuste et maintenir.

Définition et objectif
Combiner les patterns ne signifie pas utiliser tous les patterns dans chaque projet, mais savoir choisir et orchestrer les bons patterns selon le contexte et faire évoluer l'architecture progressivement.
Combiner les patterns, c’est construire une architecture claire et modulaire :
- Le POM structure vos pages et composants.
- Le Factory centralise la création d’objets (navigateurs, données, pages).
- Le Facade simplifie des scénarios complexes en un seul appel.
- Le Builder rend la création d’objets de test lisible et flexible.
- Le Screenplay donne une structure métier claire aux scénarios métier.
Objectif principal : Mettre en synergie les patterns pour créer un projet structuré, maintenable et évolutif, en commençant simple et en complexifiant au besoin.
Architecture synergique des patterns
Voici comment les patterns se complètent naturellement :

Exemple concret : Portail B2B complet
Prenons l'exemple d'un portail B2B où un utilisateur doit :
- Se connecter
- Créer un devis
- Transformer le devis en commande
- Générer une facture
Structure du projet
b2b-automation/
├── src/main/java/
│ ├── pages/ # Page Object Model
│ │ ├── LoginPage.java
│ │ ├── DashboardPage.java
│ │ └── InvoicePage.java
│ ├── data/ # Builder Pattern
│ │ ├── UserBuilder.java
│ │ └── OrderBuilder.java
│ ├── factories/ # Factory Pattern
│ │ └── TestDataFactory.java
│ ├── workflows/ # Facade Pattern
│ │ └── B2BWorkflows.java
│ └── screenplay/ # Screenplay Pattern
│ ├── actors/
│ ├── tasks/
│ └── interactions/
└── src/test/java/
└── B2BPortalTest.java
1. Page Object Model - Fondation technique
// LoginPage.java - Encapsulation simple des éléments
public class LoginPage {
private WebDriver driver;
@FindBy(id = "email")
private WebElement emailField;
@FindBy(id = "password")
private WebElement passwordField;
@FindBy(css = "[data-testid='login-button']")
private WebElement loginButton;
public void login(String email, String password) {
emailField.sendKeys(email);
passwordField.sendKeys(password);
loginButton.click();
}
}
2. Builder Pattern - Construction des données
// UserBuilder.java - Construction flexible des utilisateurs
public class UserBuilder {
private String email = "default@company.com";
private String password = "password123";
private String role = "USER";
private String company = "Test Corp";
public static UserBuilder aUser() {
return new UserBuilder();
}
public UserBuilder withEmail(String email) {
this.email = email;
return this;
}
public UserBuilder asAdmin() {
this.role = "ADMIN";
this.email = "admin@company.com";
return this;
}
public UserBuilder fromCompany(String company) {
this.company = company;
return this;
}
public User build() {
return new User(email, password, role, company);
}
}
3. Factory Pattern - Création standardisée
// TestDataFactory.java - Création des jeux de données
public class TestDataFactory {
public static User standardUser() {
return UserBuilder.aUser()
.withEmail("user@testcorp.com")
.fromCompany("Test Corp")
.build();
}
public static User adminUser() {
return UserBuilder.aUser()
.asAdmin()
.fromCompany("Test Corp")
.build();
}
public static Order simpleOrder() {
return OrderBuilder.anOrder()
.withProduct("Laptop", 2)
.withDeliveryAddress("123 Test Street")
.build();
}
}
4. Facade Pattern - Simplification des workflows
// B2BWorkflows.java - Orchestration des actions complexes
public class B2BWorkflows {
private WebDriver driver;
private LoginPage loginPage;
private DashboardPage dashboardPage;
private InvoicePage invoicePage;
public B2BWorkflows(WebDriver driver) {
this.driver = driver;
this.loginPage = new LoginPage(driver);
this.dashboardPage = new DashboardPage(driver);
this.invoicePage = new InvoicePage(driver);
}
// Workflow complet simplifié
public void completeB2BProcess(User user, Order order) {
loginAsUser(user);
createQuote(order);
convertQuoteToOrder();
generateInvoice();
}
public void loginAsUser(User user) {
loginPage.login(user.getEmail(), user.getPassword());
}
public void createQuote(Order order) {
dashboardPage.clickCreateQuote();
// Logique de création de devis
}
private void convertQuoteToOrder() {
dashboardPage.convertQuoteToOrder();
}
private void generateInvoice() {
invoicePage.generateInvoice();
}
}
5. Screenplay Pattern - Tests métiers (BDD)
// Tasks Screenplay pour actions métier
public class LoginToPortal implements Task {
private final User user;
public static LoginToPortal as(User user) {
return new LoginToPortal(user);
}
@Override
public void performAs(Actor actor) {
actor.attemptsTo(
NavigateTo.loginPage(),
Enter.theValue(user.getEmail()).into(LoginPage.EMAIL_FIELD),
Enter.theValue(user.getPassword()).into(LoginPage.PASSWORD_FIELD),
Click.on(LoginPage.LOGIN_BUTTON)
);
}
}
public class ProcessCompleteOrder implements Task {
private final Order order;
public static ProcessCompleteOrder with(Order order) {
return new ProcessCompleteOrder(order);
}
@Override
public void performAs(Actor actor) {
actor.attemptsTo(
CreateQuote.for(order),
ConvertToOrder.theCurrentQuote(),
GenerateInvoice.forCurrentOrder()
);
}
}
6. Test final - Orchestration complète
// Version Facade
@Test
public void b2bProcessWithFacade() {
// Arrange - Utilisation Factory + Builder
User user = TestDataFactory.standardUser();
Order order = TestDataFactory.simpleOrder();
// Act - Utilisation Facade
B2BWorkflows workflows = new B2BWorkflows(driver);
workflows.completeB2BProcess(user, order);
// Assert
assertTrue(workflows.isInvoiceGenerated());
}
// Version BDD (Screenplay)
@Test
public void b2bProcessWithScreenplay() {
// Given
Actor salesManager = Actor.named("Sales Manager")
.whoCan(BrowseTheWeb.with(driver));
User client = TestDataFactory.standardUser();
Order order = TestDataFactory.simpleOrder();
// When
salesManager.attemptsTo(
LoginToPortal.as(client),
ProcessCompleteOrder.with(order)
);
// Then
salesManager.should(seeThat(
TheInvoiceStatus.value(),
equalTo("GENERATED")
));
}
Conseils pour démarrer simple et évoluer
Phase 1 : Démarrage (Fondations)
// Commencer par Page Object Model simple
@Test
public void simpleLoginTest() {
LoginPage loginPage = new LoginPage(driver);
loginPage.login("user@test.com", "password");
DashboardPage dashboard = new DashboardPage(driver);
assertTrue(dashboard.isUserLoggedIn());
}
Phase 2 : Structuration (Factory + Builder)
// Ajouter Factory pour les données
@Test
public void loginWithFactory() {
User user = TestDataFactory.standardUser();
LoginPage loginPage = new LoginPage(driver);
loginPage.login(user.getEmail(), user.getPassword());
}
Phase 3 : Simplification (Facade)
java
// Introduire Facade pour workflows complexes
@Test
public void completeWorkflowTest() {
User user = TestDataFactory.adminUser();
Order order = TestDataFactory.simpleOrder();
B2BWorkflows.completeB2BProcess(user, order);
}
Phase 4 : Expressivité (Screenplay pour BDD)
// Migrer vers Screenplay si besoin BDD
@Test
public void expressiveBusinessTest() {
Actor manager = Actor.named("Sales Manager");
manager.attemptsTo(
LoginAs.standardUser(),
ProcessOrder.withStandardProducts()
);
}
Bonnes pratiques
1. Principe de progression
// ❌ Ne pas commencer par tout implémenter
public class OverEngineeredTest {
// Trop de patterns d'un coup = complexité inutile
}
// ✅ Commencer simple et évoluer
public class SimpleStartTest {
@Test
public void basicTest() {
// Page Object simple au début
LoginPage page = new LoginPage(driver);
page.login("user", "pass");
}
}
2. Choisir selon le contexte
java
// Pour tests simples : POM + Factory suffisent
// Pour workflows complexes : Ajouter Facade
// Pour BDD/collaboration : Utiliser Screenplay
// Pour données complexes : Ajouter Builder
3. Évolution guidée par les besoins
// Signaux pour évoluer :
// - Duplication de code → Factory
// - Tests difficiles à lire → Facade
// - Données complexes → Builder
// - Besoin BDD → Screenplay
4. Maintenir la simplicité
// ✅ Bon : Interface claire
public class UserWorkflows {
public void loginAsStandardUser() { /* simple */ }
public void createBasicOrder() { /* simple */ }
}
// ❌ Mauvais : Surcharge de patterns
public class OverComplexWorkflows {
// Trop de niveaux d'abstraction
}
5. Documentation vivante
// Documenter les choix d'architecture/**
* Cette classe utilise :
* - Factory : Création des utilisateurs types
* - Facade : Simplification des workflows B2B
* - Screenplay : Tests BDD expressifs
*/
public class B2BTestSuite {
// Implementation
}
FAQ
Q : Par quel pattern commencer ?
R : Toujours commencer par Page Object Model pour encapsuler les éléments UI, puis ajouter Factory quand vous avez des données de test répétitives. Les autres patterns viennent selon les besoins spécifiques.
Q : Faut-il utiliser tous les patterns dans chaque projet ?
R : Non ! Utilisez seulement ce dont vous avez besoin :
- Projet simple : POM + Factory
- Workflows complexes : Ajouter Facade
- Équipe BDD : Utiliser Screenplay
- Données complexes : Ajouter Builder
Q : Comment savoir quand ajouter un nouveau pattern ?
R : Écoutez les signaux :
- Duplication de code → Factory Pattern
- Tests difficiles à comprendre → Facade Pattern
- Construction d'objets complexe → Builder Pattern
- Besoin d'expressivité métier → Screenplay Pattern
Q : Screenplay peut-il remplacer tous les autres patterns ?
R : Non, Screenplay est complémentaire. Il structure les tests mais utilise souvent les autres patterns :
- POM pour les éléments UI
- Factory pour créer les acteurs
- Builder pour construire les données
- Facade peut être intégré dans les Tasks
Q : Comment gérer la migration d'un pattern à l'autre ?
R : Migration progressive :
// Phase 1 : POM existant
LoginPage loginPage = new LoginPage(driver);
// Phase 2 : Wrapping dans Facade
AuthWorkflows.loginAsUser(user);
// Phase 3 : Migration vers Screenplay
actor.attemptsTo(Login.as(user));
Q : Quel est le coût en performance de tous ces patterns ?
R : L'impact est négligeable comparé aux gains :
- Maintenance simplifiée
- Réutilisabilité accrue
- Évolution facilitée
- Collaboration améliorée
Le temps gagné en développement et maintenance compense largement le coût minimal en performance.
Q : Comment former l'équipe à ces patterns ?
R : Approche progressive :
- Formation théorique : Comprendre les concepts
- Pratique guidée : Commencer par POM simple
- Évolution progressive : Ajouter un pattern à la fois
- Code reviews : Valider les implémentations
- Documentation : Maintenir des exemples de référence
Q : Ces patterns fonctionnent-ils pour tous les types de tests ?
R : Oui, avec adaptation :
- Tests Web : Tous les patterns applicables
- Tests API : Screenplay + Factory + Builder
- Tests Mobile : POM adapté + Factory + Facade
- Tests de performance : Factory + Builder pour données
A retenir
La combinaison intelligente des design patterns transforme votre suite de tests en un écosystème robuste et évolutif. L'important n'est pas d'utiliser tous les patterns, mais de choisir les bons outils pour les bons problèmes et de faire évoluer l'architecture progressivement selon les besoins du projet.
Cette approche pragmatique garantit un framework de test qui grandit avec votre projet, facilitant la maintenance et la collaboration sur le long terme.
Retenez une chose : commencez simple, évoluez par étapes, et laissez vos besoins guider vos choix.