Page Object Model con Selenium WebDriver

Automatización de pruebas en Selenium usando el patron de diseño POM (Page Object Model) y Page Factory

Escribir pruebas automatizadas es más que un lujo para cualquier equipo de desarrollo de software ágil. Es una necesidad, y es una herramienta esencial para encontrar errores rápidamente durante las fases iniciales de los ciclos de desarrollo de software. Cuando hay una nueva característica que todavía está en fase de desarrollo, los desarrolladores o testers pueden ejecutar pruebas automatizadas y ver cómo se ven afectadas otras partes del sistema por esos cambios.

A través de la automatización de pruebas, es posible reducir el costo de la corrección de errores y brindar una mejora general al proceso de control de calidad (QA). Con las pruebas adecuadas, los desarrolladores tienen la oportunidad de encontrar y resolver errores incluso antes de que llegue al control de calidad. La automatización de pruebas nos ayuda a automatizar los casos de prueba y las funciones que están en constante regresión. De esta manera, los QAs tienen más tiempo para probar otras partes de la aplicación. Además, esto ayuda a garantizar la calidad del producto en las versiones de producción. Como resultado, obtenemos productos que son efectivamente más estables y un proceso de control de calidad que es más eficiente.

Si bien la escritura de pruebas automatizadas puede parecer una tarea fácil para los desarrolladores e ingenieros, aún existe la posibilidad de terminar con pruebas mal implementadas y el alto costo del mantenimiento del código en cualquier proceso ágil. Tratar de entregar constantemente cambios o funciones en cualquier proyecto de desarrollo ágil puede resultar costoso cuando se realizan pruebas (lea: estrategia de automatización de pruebas adecuada  en proyectos ágiles). Cambiar un elemento en una página web en la que se basan 20 pruebas requerirá que uno pase por estas 20 rutinas de prueba y actualice cada una para adaptarse a este cambio recién introducido. Esto no solo puede llevar mucho tiempo, sino también un factor de desmotivación grave cuando se trata de implementar pruebas automatizadas desde el principio.

Pero, ¿qué pasaría si pudiéramos realizar el cambio en un solo lugar y hacer que todas las pruebas relevantes lo utilicen? En este artículo, analizaremos las pruebas automatizadas en Selenium, y cómo podemos usar el patron de diseño Page Object Model para escribir rutinas de pruebas que se puedan mantener y reutilizar.

Page Object Model en Selenium Webdriver

El page object model es un patrón de diseño de objetos en Selenium, donde las páginas web se representan como clases y los diversos elementos de la página se definen como variables en la clase. Todas las interacciones de usuario posibles se pueden implementar como métodos en la clase:

clickLoginButton();
setCredentials(user_name,user_password);

Dado que los métodos bien nombrados en las clases son fáciles de leer, esto funciona como una forma elegante de implementar rutinas de prueba que son legibles y más fáciles de mantener o actualizar en el futuro. Por ejemplo:

Para soportar el modelo Page Object, usamos Page Factory. Page Factory es una extensión de Page Object y se puede usar de varias maneras. En este caso, utilizaremos Page Factory para inicializar los elementos web que se definen en las clases de página web u objetos de página.

Las clases de página web o los objetos de página que contienen elementos web deben inicializarse utilizando Page Factory antes de poder utilizar las variables del elemento web. Esto puede hacerse simplemente mediante el uso de initElements en PageFactory:

LoginPage page = new LoginPage(driver); 
PageFactory.initElements(driver, page);

O, incluso más simple:

Página LoginPage = PageFactory.intElements (driver, LoginPage.class)

O, dentro del constructor de clase de página web:

public LoginPage(WebDriver driver)           
         this.driver = driver; 
         PageFactory.initElements(driver, this);

Variable de WebElement con una referencia a un elemento correspondiente en la página Web actual basada en “localizadores” configurados. Esto es hecho a través del uso de anotaciones FindBy. Con esta anotación, podemos definir una estrategia de buscar el elemento, junto con la información necesaria para identificarlo:

@FindBy(how=How.NAME, using="username")
private WebElement user_name;

Cada vez que se llama a un método en esta variable WebElement, el controlador primero lo encontrará en la página actual y luego simulará la interacción. En caso de que estemos trabajando con una página simple, sabemos que encontraremos el elemento en la página cada vez que lo buscamos, y también sabemos que eventualmente saldremos de esta página y no volveremos a ella, podemos almacenar en caché el campo buscado utilizando otra anotación simple:

@FindBy(how=How.NAME, using="username")
@CacheLookup
private WebElement user_name;

Esta definición completa de la variable WebElement puede reemplazarse con su forma mucho más concisa:

@FindBy(name="username")
private WebElement user_name;

La anotación @FindBy es compatible con un puñado de otras estrategias para localizar un elemento que hacen las cosas un poco más fáciles:

id, name, className, css, tagName, linkText, partialLinkText, xpath

@FindBy(id="username")
private WebElement user_name;


@FindBy(name="passsword")
private WebElement user_password;


@FindBy(className="h3")
 private WebElement label;


@FindBy(css=”#content”)
private WebElement text;

Una vez inicializadas, estas variables de WebElement se pueden usar para interactuar con los elementos correspondientes en la página. El siguiente código será un ejemplo:

user_password.sendKeys(password); //password es una variable tipo String que contiene la contraseña

El código de arriba envía el texto contenido en la variable «password» al campo password de la página web, el siguiente código es equivalente al de arriba, pero sin hacer uso de Page Factory.

driver.findElement(By.name(“user_password”)).sendKeys(password);

Continuando, a menudo se encontrará con situaciones en las que necesita encontrar una lista de elementos en una página, y es cuando @FindBys es útil:

@FindBys(@FindBy(css=”div[class=’yt-lockup-tile yt-lockup-video’]”)))
private List<WebElement> videoElements;

Además, puede usar @FindAll con múltiples anotaciones @FindBy para buscar elementos que coincidan con cualquiera de los localizadores dados:

@FindAll({@FindBy(how=How.ID, using=”username”),
  @FindBy(className=”username-field”)})
private WebElement user_name;

Ahora que podemos representar las páginas web como clases Java y usar Page Factory para inicializar las variables de WebElement fácilmente, es hora de que podamos escribir pruebas de Selenium simples usando patrones de diseño Page Object y Page Factory.

Proyecto simple en Java con POM y Page Factory

Para nuestro proyecto simple, automaticemos el registro como freelancer en https://www.toptal.com/. Para hacer eso, necesitamos automatizar los siguientes pasos:

  1. Visite https://www.toptal.com/
  2. Dar clic en el enlace «Create a new account».
  3. Comprobar si estamos en la página de «Apply as a Freelancer»
  4. Diligenciar los campos del formulario
  5. Comprobar si estamos en la página de «Confirm Your Email»

Configuración del proyecto Selenium con POM y PageFactory

  1. Descargue e instale Java JDK
  2. Descargue e instale IntelliJ IDEA
  3. Crear un nuevo proyecto Gradle
  4. Descargar ChromeDriver

Configurar groupId y artifactId:

GroupId: com.tutorialselenium.pom

ArtifactId: com.tutorialselenium.pom

  • Agregue dependencias Selenium y JUnit en el archivo build.gradle
dependencies{
   testCompile group: 'junit', name: 'junit', version: '4.12'
   compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '2.41.0'
}

Reemplace la versión de Selenium y la versión de JUnit con los números de las últimas versiones que se pueden encontrar buscando JUnit gradle en Google y en el sitio de Selenium.

Gradle iniciará la descarga de las dependencias automáticamente.

Una vez que el proyecto se haya iniciado, podemos comenzar a crear nuestros paquetes de prueba en «src/test/java». Nombra los paquetes «pages» y «tests».

Mantendremos nuestras clases de Page Object/Page Factory en «pages» y los scripts de prueba en «tests».

Dentro del paquete de resources que se crea por defecto, creamos una nueva carpeta llamada «drivers» en ella vamos a colocar el ChromeDriver que descargamos. Nos debe quedar de la siguiente forma:

Ahora, podemos empezar a crear nuestras clases page objects.

Page Object HomePage

Lo primero que necesitamos implementar es la página de Home de https://www.toptal.com. Cree una clase en «pages» y asígnele el nombre «HomePage».

Al localizar el link «APPLY AS A FREELANCER» de la parte superior de la pagina, usamos Xpath y nos ayudamos del predicador child, todo esto lo puedes aprender en el siguiente enlace XPath en Selenium WebDriver: Tutorial completo

package pages;

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;

    //LOCALIZADORES
    //Enlace "Create new account"
    @FindBy(how = How.XPATH,using = "//nav[@class='page_header_top-nav_wrapper']//child::li[2]")
    WebElement linkApplyFreelancer;

    //Constructor
    public HomePage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    public void clickLinkApplyFreelancer(){
        linkApplyFreelancer.click();
    }

}

Determinar los localizadores de elementos

En la página de inicio de Toptal nos interesa un elemento en particular, y ese es el botón «Apply as a Freelancer». Podemos encontrar este elemento haciendo coincidir el texto, que es lo que estamos haciendo arriba. Al modelar páginas web como clases de objetos de página, encontrar e identificar elementos a menudo puede convertirse en una tarea. Para lo cual recomiendo leer las siguientes entradas que te ayudarán a encontrar los elementos de una página de la mejor forma.

PageObject SignUpPage

Definimos un PageObject que representa la página «Join Freelancer» que representa el formulario de registro. Ya que tenemos que tratar con varios campos de formulario, definimos una variable WebElement para cada campo de formulario. Encontramos cada campo por su «id» o «xpath» y definimos métodos genéricos (clic y mandar texto a los input) para usarlos en los campos y así simular las acciones del usuario.

package pages;

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 JoinFreelancerPage {
    private WebDriver driver;

    //Localizadores
    @FindBy(how = How.XPATH,using = "//h1")
    WebElement titlePage;

    @FindBy(how = How.XPATH,using = "//*[@data-role='custom_field']")
    WebElement txtSelectTalent;

    @FindBy(how = How.XPATH,using = "//*[@data-value='developers']")
    WebElement optionDeveloper;

    @FindBy(how = How.ID,using = "talent_create_applicant_email")
    WebElement txtEmail;

    @FindBy(how = How.ID,using = "talent_create_applicant_password")
    WebElement txtPassword;

    @FindBy(how = How.ID,using = "talent_create_applicant_password_confirmation")
    WebElement txtPasswordConfirmation;

    @FindBy(how = How.ID,using = "talent_create_applicant_full_name")
    WebElement txtFullName;

    @FindBy(how = How.XPATH,using = "//span[@class='label_wrap']")
    WebElement checkConfidential;

    @FindBy(how = How.ID,using = "save_new_talent_create_applicant")
    WebElement btnJoin;

    public JoinFreelancerPage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    public void fillFormJoin(String fullName,String email,String password){

        clickOnElement(txtSelectTalent);
        clickOnElement(optionDeveloper);
        sendText(txtEmail,email);
        sendText(txtPassword,password);
        sendText(txtPasswordConfirmation,password);
        sendText(txtFullName,fullName);
        clickOnElement(checkConfidential);
        clickOnElement(btnJoin);
    }

    //Metodo generico para dar clic a un elemento
    public void clickOnElement(WebElement element){
        element.click();
    }

    //Metodo generico para enviar texto a los input
    public void sendText(WebElement element,String text){
        element.clear();
        element.sendKeys(text);
    }

    public boolean isPageOpened(String title){
        return titlePage.getText().contains(title);
    }
}

Escribir un test simple

Con las clases Object Page que representan nuestras páginas, y las interacciones de los usuarios como sus métodos, ahora podemos escribir nuestro script de prueba simple como una serie de llamadas a métodos y aserciones.

package tests;

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.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import pages.HomePage;
import pages.JoinFreelancerPage;

public class JoinFreelancerTest {

    private String PATHDRIVER = "src/test/resources/drivers/";
    private String baseURL = "https://www.toptal.com/";
    WebDriver driver;

    @Before
    public void setup(){
        System.setProperty("webdriver.chrome.driver",PATHDRIVER+"chromedriver.exe");
        ChromeOptions options = new ChromeOptions();
        options.addArguments("start-maximized");
        options.addArguments("--incognito");
        driver = new ChromeDriver(options);
    }

    @Test
    public void signUp(){
        driver.get(baseURL);
        //Crear objeto de las clases Page
        HomePage homePage = new HomePage(driver);
        JoinFreelancerPage joinFreelancer = new JoinFreelancerPage(driver);

        //Hacemos clic en el enlace "Apply as a Freelancer"
        homePage.clickLinkApplyFreelancer();
        //Verificamos que la página "Apply as a Freelancer" se haya abierto
        Assert.assertTrue(joinFreelancer.isPageOpened("Apply to Join our Network of"));
        //Llenamos el formulario usando el metodo que creamos en JoinFreelancerPage
        joinFreelancer.fillFormJoin("Tutorial Selenium","admin@tutorialselenium.com","tutorialselenium");
        //Comprobamos si se creó la cuenta exitosamente verificando que estamos en la url "Confirm Your Email"
        Assert.assertTrue(driver.getCurrentUrl().contains("https://www.toptal.com/talent/check_confirmation"));
    }

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

Ejecutar el Test

En este punto, la estructura de su proyecto debería verse así:

Si desea ejecutar la prueba, seleccione «JoinFreelancerTest» de la estructura del proyecto, haga clic derecho sobre él y luego seleccione Run  «JoinFreelancerTest».

NOTA IMPORTANTE: La prueba puede fallar porque al momento de darle clic en el botón «Join Toptal» la pagina te puede pedir comprobación de recaptcha.  Para efecto del ejemplo, nos quisimos centrar en la estructura y como capturar los elementos.

Conclusión

Page Object y Page Factory facilitan el modelado de páginas web en Selenium, simplifican mucho más la vida de los desarrolladores y los QA. Cuando se realiza correctamente, estas clases de objetos de página se pueden reutilizar en todo su conjunto de pruebas y para brindarle la oportunidad de implementar las pruebas automatizadas de Selenium para sus proyectos desde el principio, sin comprometer el desarrollo ágil. Al abstraer las interacciones de los usuarios en los modelos de objetos de su página y mantener sus rutinas de prueba ligeras y simples, puede adaptar su conjunto de pruebas a los requisitos cambiantes con poco esfuerzo.

Espero haber logrado mostrarle cómo escribir un código de prueba agradable y limpio que sea fácil de mantener. Terminaré el artículo con mi cita de control de calidad favorita: ¡Piense dos veces, codifique una vez!

 

 

Compartir artículo

Leave Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.