Creación de un Page Object Model con Selenium, TestNG y Maven

Creació d’un Page Object Model amb Selenium, TestNG i Maven

Durant anys treballant en la indústria de la qualitat de software, m’he adonat que la majoria de les persones que comencen en aquest apassionant món d’automatització de proves, no té clar com crea i/o generar el seu propi POM (Page Object Model), com les seves pròpies sigles indiquen és un concepte de model basat en pàgines i molts es preguntaran: Quines pàgines? Doncs bé, això és tenir una pàgina amb els mètodes únics i no susceptibles de repetició amb cap altra de la web, fent que sigui fàcil de fer anar, entendre i reutilitzar.

Per tant, el POM es tracta d’un concepte que ve per facilitar el desenvolupament i la implementació de les proves.

Veiem un exemple

Imaginem una web amb tres pàgines, una de home, una altra de login i una altra per exemple de preus:

Creación de un Page Object Model

Llavors en la nostra implementació tindrem tres classes, una per pàgina on estaran les variables necessàries per a les mateixes i els seus mètodes únics i dic únics perquè tots aquells mètodes susceptibles de repetició els ficarem en una classe a part anomenada per exemple utility, dins d’un directori que li podem cridar hook, el qual a més de tenir aquests utilities també tindrà una classe de la qual tiraran tots els test, aquesta classe contindrà un mètode @beforeTest i un altre @afterTest, a més d’altres possibles.

Creación de un Page Object Model

Anem al gra:

Nota: utilitzarem la versió community de Intellij, atès que és la versió gratuïta

Descripció de les proves a executar:

  1. Navegar a la pàgina login des de la pàgina home, loguejar-se amb un email erroni i validar que el missatge existeix i està desplegat
  2. Navegar a la pàgina Price des de la home i validar que aquesta mostra un missatge específic

Nota: a més en ambdues es comprovaran les corresponents urls de cada pàgina així com els titles d’aquestes

  1. Crear un nou Projecte:
    Inteilij -> new proyect -> mave -> Create from archetype

    Creación de un Page Object Model
  2. Crear el directori drivers amb el subdirectori Chrome i dins colocar el driver de chromedriver.exe, el qual descarregarem de la propia web de chromedriver, ens haurem de fixar be en la versió de Chrome que tenim instalada.

    Creación de un Page Object Model
  3. Insertarem les dependèncias en el pom.xml

    A.- testng
    B.- selenium-java
    C.- junit
    D.- javafaker

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>org.example</groupId>
        <artifactId>repaso</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>

        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.testng/testng -->
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>7.3.0</version>
                <scope>test</scope>
            </dependency>

            <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
            <dependency>
                <groupId>org.seleniumhq.selenium</groupId>
                <artifactId>selenium-java</artifactId>
                <version>3.141.59</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>

            <!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
            <dependency>
                <groupId>com.github.javafaker</groupId>
                <artifactId>javafaker</artifactId>
                <version>1.0.2</version>
            </dependency>

        </dependencies>

    </project>
  4. Crearem la estructura d’arbre per al nostre POM:
    A.- Generar tres subdirectoris des de src/test/java
    1.- hook, posarem els mètodes @BeforeTest y @AfterTest
    2.- pages, tindrem una classe per cada pàgina de la web a testar
    3.- Nom de la web a automatizar, exemple shopTest, on estaran els test.

    Creación de un Page Object Model

  5. Crearem la classe hook dins el directori hook:
    Aquesta classe contindrà:

    A.- Instància del webdriver, amb el que podrem invocar els mètodes per poder trebalar amb la interfície
    B.- Seteo del properties, on li direm el path al driver de Chrome en el nostre projecte
    C.- Instància del ChromeDriver
    D.- Mètodes get/close, per aixecar el navegador, obrir la web i tanquem el navegador
    E.- Espera implícita, per a que cada cop que executem qualsevol mètode del driver
    F.- Instància a la home o landingPage del nostre projecte

  6. Codi de la classe Hook:

    Nota: aquesta classe contindrà els mètodes abans i després de cada test, que s’hauran d’executar per al correcte funcionament.

    package pageObject.hook;

    import pageObject.pages.HomePage;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.testng.annotations.AfterTest;
    import org.testng.annotations.BeforeTest;

    import java.util.concurrent.TimeUnit;

    public class Hook {
        public WebDriver driver;
        public pageObject.pages.HomePage HomePage;
        public Utility util;


        @BeforeTest
        public void setUp(){
            System.setProperty("webdriver.chrome.driver","drivers/Chrome/chromedriver.exe");
            driver = new ChromeDriver();
            driver.get("https://www.shopify.com");
            driver.manage().window().maximize();
            driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
            HomePage = new HomePage(driver);
            util = new Utility(driver);
        }

        @AfterTest
        public void close(){
            driver.close();
        }
    }

  7. Creació dins del directori pages amb les següents classes:

    Nota: recordeu que aquest haurà de contenir una classe per pàgina de la web, a més les variables amb els localitzadors i els mètodes necessaris per a les proves necessaries sobre cada pàgina.
    A.- homePage
    B.- loginPage
    C.- pricingPage



    7.1 Classe homepage:

    package pageObject.pages;

    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;

    public class HomePage {
        WebDriver driver;

        String tabPricing="//*[@href='/pricing']";
        String tabLoginLinkText="Log in";

        public HomePage(WebDriver remoteDriver){
            driver = remoteDriver;
        }

        public PricingPage clickBtnPricing() {
                driver.findElement(By.xpath(tabPricing)).click();
                PricingPage nextPage = new PricingPage(driver);
                return nextPage;
        }

        public LoginPage clickBtnLogin() {
                driver.findElement(By.linkText(tabLoginLinkText)).click();
                LoginPage nextPage = new LoginPage(driver);
                return nextPage;
        }
    }

    7.2.- Classe LoginPage:

    package pageObject.pages;

    import com.github.javafaker.Faker;
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;

    public class LoginPage {

        WebDriver driver;
        Faker faker = new Faker();
        String idInputFormEmail="shop_domain";
        String Email= faker.internet().emailAddress();
        String nextXpath="//*[@name='commit']";
        String messErrorCss="validation-error__message";

        public LoginPage(WebDriver remoteDriver){
            driver = remoteDriver;
        }

        public void sendKeys() {
            WebElement input = driver.findElement(By.id(idInputFormEmail));
            input.sendKeys(Email);
        }

        public void submitNextBtn(){
            WebElement btnNext = driver.findElement(By.xpath(nextXpath));
            btnNext.click();
        }

        public boolean messageErrorDisplayed(){
            WebElement message = driver.findElement(By.className(messErrorCss));
            boolean exist = message.isDisplayed();
            return exist;
        }
    }

    7.3.- Classe PricingPage :

    package pageObject.pages;

    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;

    import java.util.List;

    public class PricingPage {
        WebDriver driver;

        String findText="Set up your store, pick a plan later";

        public PricingPage(WebDriver remoteDriver){
            driver = remoteDriver;
        }

        public boolean findH1() {
            boolean exist = false;
            List<WebElement> h1 = driver.findElements(By.tagName("h1"));
            for (WebElement element : h1) {
                if (element.getText().equals(findText)) {
                    exist = true;
                }
            }
            return exist;
        }
    }

  8. Directori shopTest:
    8.1- Classe shopLoginTest:
    Nota: aquestes classes contindran els executables de cada test. Per a que això funcioni es necessari la anotació @ Test abans de cada mètode.

    import pageObject.hook.Hook;
    import pageObject.pages.HomePage;
    import pageObject.pages.LoginPage;
    import org.testng.Assert;
    import org.testng.annotations.Test;

    public class ShopLoginTest extends Hook {

        String titleTextLogin = "Shopify - Login";
        String urlLogin = "https://accounts.shopify.com/store-login";

        @Test
        public void emailErrorLoginTest(){
            //do click on the login bottom
            pageObject.pages.HomePage HomePage = new HomePage(driver);
            LoginPage LoginPage = HomePage.clickBtnLogin();

            //Check the url and the title
            boolean findUrl = util.getUrl(urlLogin);
            Assert.assertTrue(findUrl);

            boolean findTitle = util.getTitle(titleTextLogin);
            Assert.assertTrue(findTitle);

            //write a faker email and do click on next bottom
            LoginPage.sendKeys();
            LoginPage.submitNextBtn();

            //Check the error is deployed
            boolean exist = LoginPage.messageErrorDisplayed();
            Assert.assertTrue(exist);
        }
    }

    8.2.- Classe ShopPriceTest:

    package pageObject.shopTest;

    import org.testng.Assert;
    import org.testng.annotations.Test;
    import pageObject.hook.Hook;
    import pageObject.pages.PricingPage;

    public class ShopPriceTest extends Hook {

        String titleTextHome = "Start a Business, Grow Your Business - Shopify 14-Day Free Trial";
        String urlHome="https://www.shopify.com/";

        String urlPricing="https://www.shopify.com/pricing";

        @Test
        public void pricingTest(){
            //Check the url and the title
            boolean findUrl = util.getUrl(urlHome);
            Assert.assertTrue(findUrl);

            boolean findTitle = util.getTitle(titleTextHome);
            Assert.assertTrue(findTitle);

            //Do click on the price tab menu
            PricingPage PricingPage = HomePage.clickBtnPricing();

            //Check the new url
            boolean findUrlPricing = util.getUrl(urlPricing);
            Assert.assertTrue(findUrlPricing);

            //Check exist a new specific text
            boolean exist = PricingPage.findH1();
            Assert.assertTrue(exist);
        }
    }

  9. Directori hook:
    9.1.- Classe Utility:
    Classe que contindrà tots els mètodes susceptibles de que siguin repetits en alguna part del codi, així evitarem el codi duplicat..

    package pageObject.hook;

    import org.openqa.selenium.WebDriver;

    public class Utility {

        WebDriver driver;

        public Utility(WebDriver remoteDriver){
            driver = remoteDriver;
        }

        public boolean getTitle(String titleText){
            String title = driver.getTitle();
            boolean findText = title.contains(titleText);
            return findText;
        }

        public boolean getUrl(String urlLoginExpected){
            String Url = driver.getCurrentUrl();
            boolean findUrl = Url.contains(urlLoginExpected);
            return findUrl;
        }
    }

    9.2.- Classe Hook:

    Classe que contindrà la anotació @BeforeTest amb el mètode setUp el qual en el nostre cas, s’encarregarà abans de cada test de setear les properties, inicialitzar el driver, executar el mètode get() e inicializar las instancias necesarias.
    @afterTest, contendrá en nuestro caso únicamente el método close(), para cerrar el navegador cada vez que termine un test.

    package pageObject.hook;

    import pageObject.pages.HomePage;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.testng.annotations.AfterTest;
    import org.testng.annotations.BeforeTest;

    import java.util.concurrent.TimeUnit;

    public class Hook {
        public WebDriver driver;
        public pageObject.pages.HomePage HomePage;
        public Utility util;

        @BeforeTest
        public void setUp(){
            System.setProperty("webdriver.chrome.driver","drivers/Chrome/chromedriver.exe");
            driver = new ChromeDriver();
            driver.get("https://www.shopify.com");
            driver.manage().window().maximize();
            driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
            HomePage = new HomePage(driver);
            util = new Utility(driver);
        }

        @AfterTest
        public void close(){
            driver.close();
        }
    }

Referències:

Naila Ros
Fernando Manrique Villanueva
QA Tester