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

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

Durante años trabajando en la industria de la calidad de software, me he dado cuenta de que la mayoría de las personas que comienzan en este apasionante mundo de automatización de pruebas, no tiene claro como crea y/o generar su propio POM (Page Object Model), como sus propias siglas indican es un concepto de modelo basado en páginas y muchos se preguntaran, ¿Qué páginas? Pues bien, esto no es ni más ni menos que tener una página con los métodos únicos y no susceptibles de repetición con ninguna otra de la web, haciendo del mismo que sea fácil de manejar, entender y reutilizar.

Por lo tanto, el POM se trata de un concepto que viene para facilitar el desarrollo y la implementación de las pruebas.

Veamos un ejemplo:

Imaginemos una web con tres páginas, una de home, otra de login y otra por ejemplo de precios:

Creación de un Page Object Model

Entonces en nuestra implementación tendremos tres clases, una por página donde estarán las variables necesarias para las mismas y sus métodos únicos y digo únicos puesto que todos aquellos métodos susceptibles de repetición los meteremos en una clase aparte llamada por ejemplo utility, dentro de un directorio que le podemos llamar hook, el cual además de tener dichos utilities también tendrá una clase de la cual tirarán todos los test, dicha clase contendrá un método @beforeTest y otro @afterTest, además de otros posibles.

Creación de un Page Object Model

Vayamos al grano:

Nota: utilizaremos la versión community de Intellij, dado que es la versión gratuita

Descripción de las pruebas a ejecutar:

  1. Navegar a la página login desde la página home, logearnos con un mail erróneo y validar que el mensaje existe y esta desplegado
  2. Navegar a la página Price desde la home y validar que esta muestra un mensaje específico

Nota: además en ambas se van a comprobar las pertenecientes url de cada página así como los titles de las mismas

  1. Crear nuevo Proyecto:
    Inteilij -> new proyect -> mave -> Create from archetype

    Creación de un Page Object Model
  2. Crear el directorio drivers con el subdirectorio Chrome y dentro colocar el driver de chromedriver.exe, el cual descargaremos desde la propia web de chromedriver, fijándonos en la versión de Chrome que tenemos instalada.

    Creación de un Page Object Model
  3. Insertamos la dependencias 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. Creamos la estructura de árbol para nuestro POM:
    A.- Generar tres subdirectorios desde src/test/java
    1.- hook, pondremos los métodos @BeforeTest y @AfterTest
    2.- pages, tendremos una clase por cada página de la web a testear
    3.- Nombre de la web a automatizar, ejemplo shopTest , donde estarán los test.

    Creación de un Page Object Model

  5. Creamos la clase hook dentro del directorio hook:
    Esta clase contendrá:

    A.- Instancia del webdriver, con el que podremos invocar los métodos para poder trabajar con la interfaz
    B.- Seteo del properties, donde le decimos el path al driver de Chrome en nuestro proyecto
    C.- Instancia del ChromeDriver
    D.- Métodos get/close, para levantar el navegador, abrir la web y cerrar el navegador
    E.- Espera implícita, para cada vez que ejecutemos cualquier método del driver
    F.- Instancia a la home o landingPage de nuestro proyecto

  6. Código de la clase Hook:

    Nota: esta clase contendrá los métodos antes y después de cada test, que deberán ejecutarse para el correcto funcionamiento.

    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ón dentro del directorio pages con las siguientes clases:

    Nota: recordad este deberá contener una clase por página de la web, además las variables con los localizadores y los métodos necesarios para las pruebas sobre cada página
    A.- homePage
    B.- loginPage
    C.- pricingPage


    Creación de un Page Object Model

    7.1 Clase 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.- Clase 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.- Clase 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. Directorio shopTest:
    8.1- Clase ShopLoginTest:
    Nota: Estas clases contendran los ejecutables de cada test, para que esto funcione es necesario la anotación @ Test ante de cada método

    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.- Clase 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. Directorio hook:
    9.1.- Clase Utility:
    Clase que contendrá todos los métodos susceptibles de que sea repetidos en alguna parte del código, así evitaremos las duplicidades de código.

    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.- Clase Hook:

    Clase que contendrá la anotación @BeforeTest con el método setUp el cual en nuestro caso, se encargará antes de cada test de setear las properties, inicializar el driver, ejecutar el método 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();
        }
    }

     

Referencias:

Naila Ros
Fernando Manrique Villanueva
QA Tester