Automatisation dans Selenium : modèle d'objet de page et fabrique de pages
Publié: 2022-03-11L'écriture de tests automatisés est plus qu'un simple luxe pour toute équipe de développement logiciel agile. C'est un besoin, et c'est un outil essentiel pour trouver rapidement les bugs au cours des premières phases des cycles de développement logiciel. Lorsqu'une nouvelle fonctionnalité est encore en phase de développement, les développeurs peuvent exécuter des tests automatisés et voir comment d'autres parties du système sont affectées par ces modifications. Cet article explique comment accélérer les tests automatisés à l'aide du modèle d'objet de page dans Selenium.
Grâce à l'automatisation des tests, il est possible de réduire le coût de la correction des bogues et d'apporter une amélioration globale au processus d'assurance qualité (AQ) du logiciel. Avec des tests appropriés, les développeurs ont la possibilité de trouver et de résoudre les bogues avant même qu'ils n'atteignent l'AQ. L'automatisation des tests nous aide en outre à automatiser les cas de test et les fonctionnalités qui régressent constamment. De cette façon, les AQ ont plus de temps pour tester d'autres parties de l'application. De plus, cela aide à garantir la qualité du produit dans les versions de production. En conséquence, nous obtenons des produits effectivement plus stables et un processus d'assurance qualité plus efficace.
Bien que l'écriture de tests automatisés puisse sembler une tâche facile pour les développeurs et les ingénieurs, il existe toujours la possibilité de se retrouver avec des tests mal implémentés et le coût élevé de la maintenance du code dans tout processus agile. Essayer de fournir constamment des modifications ou des fonctionnalités dans tout projet de développement agile peut s'avérer coûteux lorsque des tests sont impliqués. Changer un élément sur une page Web sur laquelle 20 tests reposent nécessitera de passer par ces 20 routines de test et de mettre à jour chacune pour s'adapter à ce changement nouvellement introduit. Non seulement cela peut prendre beaucoup de temps, mais c'est aussi un sérieux facteur de démotivation lorsqu'il s'agit de mettre en œuvre des tests automatisés dès le début.
Mais que se passerait-il si nous pouvions effectuer le changement à un seul endroit et que chaque routine de test pertinente l'utilise ? Dans cet article, nous examinerons les tests automatisés dans Selenium et comment nous pouvons utiliser les modèles Page Object pour écrire des routines de test maintenables et réutilisables.
Modèle d'objet de page dans Selenium
Le modèle d'objet de page est un modèle de conception d'objet dans Selenium, où les pages Web sont représentées sous forme de classes et les différents éléments de la page sont définis comme des variables de la classe. Toutes les interactions utilisateur possibles peuvent alors être implémentées sous forme de méthodes sur la classe :
clickLoginButton(); setCredentials(user_name,user_password);
Étant donné que les méthodes bien nommées dans les classes sont faciles à lire, cela fonctionne comme un moyen élégant d'implémenter des routines de test qui sont à la fois lisibles et plus faciles à maintenir ou à mettre à jour à l'avenir. Par exemple:
Afin de prendre en charge le modèle Page Object, nous utilisons Page Factory. Page Factory dans Selenium est une extension de Page Object et peut être utilisée de différentes manières. Dans ce cas, nous utiliserons Page Factory pour initialiser les éléments Web définis dans les classes de page Web ou les objets de page.
Les classes de page Web ou les objets de page contenant des éléments Web doivent être initialisés à l'aide de Page Factory avant que les variables d'élément Web puissent être utilisées. Cela peut être fait simplement en utilisant la fonction initElements sur PageFactory :
LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);
Ou, encore plus simple :
LoginPage page = PageFactory.intElements(driver,LoginPage.class)
Ou, à l'intérieur du constructeur de classe de page Web :
public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }
Page Factory initialisera chaque variable WebElement avec une référence à un élément correspondant sur la page Web réelle en fonction des « localisateurs » configurés. Cela se fait grâce à l'utilisation des annotations @FindBy . Avec cette annotation, on peut définir une stratégie de recherche de l'élément, ainsi que les informations nécessaires pour l'identifier :
@FindBy(how=How.NAME, using="username") private WebElement user_name;
Chaque fois qu'une méthode est appelée sur cette variable WebElement , le pilote va d'abord la trouver sur la page en cours, puis simuler l'interaction. Dans le cas où nous travaillons avec une page simple, nous savons que nous trouverons l'élément sur la page à chaque fois que nous le chercherons, et nous savons également que nous finirons par quitter cette page et ne pas y revenir, nous pouvons mettre en cache le champ recherché en utilisant une autre annotation simple :
@FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;
Cette définition complète de la variable WebElement peut être remplacée par sa forme beaucoup plus concise :
@FindBy(name="username") private WebElement user_name;
L'annotation @FindBy prend en charge une poignée d'autres stratégies qui facilitent un peu les choses :
id, name, className, css, tagName, linkText, partialLinkText, xpath
@FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;
Une fois initialisées, ces variables WebElement peuvent ensuite être utilisées pour interagir avec les éléments correspondants sur la page. Le code suivant sera, par exemple :
user_password.sendKeys(password);
… envoie la séquence donnée de frappes au champ du mot de passe sur la page, et cela équivaut à :
driver.findElement(By.name(“user_password”)).sendKeys(password);
Ensuite, vous rencontrerez souvent des situations où vous devez trouver une liste d'éléments sur une page, et c'est là que @FindBys est utile :
@FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;
Le code ci-dessus trouvera tous les éléments div ayant deux noms de classe "yt-lockup-tile" et "yt-lockup-video". Nous pouvons encore simplifier cela en le remplaçant par ce qui suit :
@FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;
De plus, vous pouvez utiliser @FindAll avec plusieurs annotations @FindBy pour rechercher des éléments correspondant à l'un des localisateurs donnés :
@FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;
Maintenant que nous pouvons représenter des pages Web sous forme de classes Java et utiliser Page Factory pour initialiser facilement les variables WebElement , il est temps de voir comment nous pouvons écrire des tests Selenium simples en utilisant le modèle Page Object et Page Factory.
Projet d'automatisation de test Selenium simple en Java
Pour notre didacticiel sur le modèle d'objet de page, automatisons l'inscription des développeurs à Toptal. Pour ce faire, nous devons automatiser les étapes suivantes :
Visitez www.toptal.com
Cliquez sur le bouton "Postuler en tant que développeur"
Sur la page du portail, vérifiez d'abord si elle est ouverte
Cliquez sur le bouton "Rejoindre Toptal"
Remplir le formulaire
Soumettez le formulaire en cliquant sur le bouton "Rejoindre Toptal"
Configurer un projet
Téléchargez et installez Java JDK
Téléchargez et installez InteliJ Idea
Créer un nouveau projet Maven
Liez « Project SDK » à votre JDK, par exemple : sous Windows « C:\Program Files\Java\jdkxxx »
Configurez l'ID de groupe et l'ID d'artefact :
<groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
- Ajoutez les dépendances Selenium et JUnit Maven dans votre fichier POM de projet
<dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>
Remplacez la version Selenium et la version JUnit par les derniers numéros de version que vous pouvez trouver en recherchant JUnit Maven sur Google et sur le site Selenium.
À ce stade, si la construction automatique est activée, les dépendances devraient commencer à se télécharger automatiquement. Sinon, activez simplement Plugins > install > install: install sous le panneau Maven Projects sur le côté droit de votre IDE IntelliJ Idea.

Une fois le projet démarré, nous pouvons commencer à créer notre package de test sous « src/test/java ». Nommez le package « com.toptal » et créez-y deux autres packages : « com.toptal.webpages » et « com.toptal.tests ».
Nous conserverons nos classes Page Object/Page Factory sous « com.toptal.webpages » et les routines de test sous « com.toptal.tests ».
Maintenant, nous pouvons commencer à créer nos classes Page Object.
Objet de la page d'accueil
Le tout premier que nous devons implémenter est pour la page d'accueil de Toptal (www.toptal.com). Créez une classe sous "com.toptal.webpages" et nommez-la "HomePage".
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }
Détermination des localisateurs d'éléments
Sur la page d'accueil de Toptal, nous nous intéressons à un élément en particulier, à savoir le bouton "Postuler en tant que développeur". Nous pouvons trouver cet élément en faisant correspondre le texte, ce que nous faisons ci-dessus. Lors de la modélisation de pages Web en tant que classes d'objets de page, la recherche et l'identification d'éléments peuvent souvent devenir une corvée. Avec Google Chrome ou les outils de débogage de Firefox, cela peut être facilité. En cliquant avec le bouton droit sur n'importe quel élément d'une page, vous pouvez activer l'option "Inspecter l'élément" du menu contextuel pour obtenir des informations détaillées sur l'élément.
Un moyen courant (et mon préféré) consiste à rechercher des éléments à l'aide de l'extension FireBug de Firefox, en combinaison avec le pilote Web Firefox dans Selenium. Après avoir installé et activé l'extension FireBug, vous pouvez cliquer avec le bouton droit sur la page et sélectionner "Inspecter l'élément avec FireBug" pour ouvrir FireBug. À partir de l'onglet HTML de FireBug, vous pouvez copier le XPath, le chemin CSS, le nom de la balise ou "l'ID" (si disponible) de n'importe quel élément de la page.
En copiant le XPath de l'élément dans la capture d'écran ci-dessus, nous pouvons créer un champ WebElement pour celui-ci dans notre Page Object comme suit :
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;
Ou pour simplifier les choses, nous pouvons utiliser ici le nom de balise "h1", tant qu'il identifie de manière unique l'élément qui nous intéresse :
@FindBy(tagName = "h1") WebElement heading;
Objet de page DeveloperPortalPage
Ensuite, nous avons besoin d'un objet de page qui représente la page du portail des développeurs, auquel nous pouvons accéder en cliquant sur le bouton "Appliquer en tant que développeur".
Sur cette page, nous avons deux éléments d'intérêt. Pour déterminer si la page a été chargée, nous souhaitons vérifier l'existence de l'en-tête. Et nous voulons également un champ WebElement pour le bouton "Rejoindre Toptal".
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }
Objet de page DeveloperApplyPage
Et enfin, pour notre troisième et dernier objet de page pour ce projet, nous en définissons un qui représente la page contenant le formulaire de candidature du développeur. Comme nous devons traiter ici un certain nombre de champs de formulaire, nous définissons une variable WebElement pour chaque champ de formulaire. Nous trouvons chaque champ par leur "id" et nous définissons des méthodes de setter spéciales pour chaque champ qui simulent les frappes au clavier pour les champs correspondants.
package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }
Rédaction d'un test de sélénium simple
Avec les classes Page Object représentant nos pages et les interactions utilisateur comme méthodes, nous pouvons maintenant écrire notre routine de test simple sous la forme d'une série d'appels et d'assertions de méthode simples.
package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }
Exécution du test
À ce stade, la structure de votre projet devrait ressembler à ceci :
Si vous souhaitez exécuter le test, sélectionnez "ApplyAsDeveloperTest" dans l'arborescence, faites un clic droit dessus, puis sélectionnez Run 'ApplyAsDeveloperTest' .
Une fois le test exécuté, vous pouvez voir les résultats dans le coin inférieur gauche de votre IDE :
Conclusion
Page Object et Page Factory facilitent la modélisation des pages Web dans Selenium et les testent automatiquement et simplifient la vie des développeurs et des QA. Lorsqu'elles sont bien faites, ces classes d'objets de page peuvent être réutilisées dans l'ensemble de votre suite de tests et vous donner la possibilité d'implémenter des tests Selenium automatisés pour vos projets dès le début, sans compromettre le développement agile. En faisant abstraction des interactions des utilisateurs dans vos modèles d'objets de page et en gardant vos routines de test légères et simples, vous pouvez adapter votre suite de tests à l'évolution des exigences avec peu d'effort.
J'espère avoir réussi à vous montrer comment écrire un code de test agréable et propre, facile à entretenir. Je terminerai l'article avec ma citation QA préférée :
Réfléchissez à deux fois, codez une fois !