Diferencia entre revisiones de «Selenium con DSL»

De Dos Ideas.
Saltar a: navegación, buscar
(Ejemplo)
Línea 3: Línea 3:
  
 
== Ejemplo ==
 
== Ejemplo ==
 +
A continuación mostramos una propuesta de como encarar un test de [[Selenium]]con [[DSL]]. Para el ejemplo, suponemos un sistema de biblioteca, con las siguientes pantallas y su navegación.
 +
Se puede observar que pare este sistemas planteamos dos roles de usuario, el Bibliotecario y el Socio.
  
A continuación mostramos una propuesta de como encarar un test de [[Selenium]] con [[DSL]]. Para el ejemplo, suponemos una aplicación con seguridad, usuarios con diferentes Roles, y diferentes pantallas:
 
  
=== AltaDenunciaSeleniumTest ===
+
=== Creando un test ===
 +
En el siguiente test, vamos a probar la funcionalidad de la reserva de libro.
 +
Para escribir el siguiente test, se utilizó una sintaxis acotada y simple con las siguientes reglas:
 +
 
 +
* '''ir'''''<NombreDeLaPagina>'': Los métodos que comiencen con el prefijo “ir”, nos retorna el nuevo contexto (página).
 +
* '''verificar'''''<Descripción>'':  Los métodos que comiencen con el prefijo "verificar", poseen los “assert” para comprobar alguna acción realizada en la página, este método siempre retona la misma página (this).
 +
* '''ingresarComo'''''<Nombre del Rol>'': Rol con el que se probará el sistema en la prueba.
 +
* '''aceptar, cancelar, volver, buscar''': Son nombre de las acciones básica que se realizan en un página y pueden (o nó) devolver otra página.
 +
* ''<acciones propias de la página>'' : Estos métodos realizan acciones sobre el sistema sin ir a otra página. Ej.: reservar
 +
 
 +
 
 +
==== ReservaSeleniumDslTest ====
 
<code java>
 
<code java>
package com.test;
+
package com.test.dsl;
 
 
import static org.junit.Assert.fail;
 
  
 
import org.junit.AfterClass;
 
import org.junit.AfterClass;
 +
import org.junit.Assert;
 
import org.junit.BeforeClass;
 
import org.junit.BeforeClass;
 
import org.junit.Test;
 
import org.junit.Test;
  
import com.test.selenium.dsl.Usuario;
 
 
public class AltaDenunciaSeleniumTest {
 
  
 +
public class ReservaSeleniumDslTest {
 
      
 
      
 
     @BeforeClass
 
     @BeforeClass
Línea 25: Línea 34:
 
         Usuario.inicializar();
 
         Usuario.inicializar();
 
     }
 
     }
 
+
 
     @AfterClass
 
     @AfterClass
 
     public static void tearDownClass() throws Exception {
 
     public static void tearDownClass() throws Exception {
 
         Usuario.finalizar();
 
         Usuario.finalizar();
 
     }
 
     }
   
+
 
     @Test
 
     @Test
     public void ingresarConRolAdministrativo_SeDaDeAltaUnaDenuncia() {
+
     public void comoBibliotecarioQuieroRealizarUnaReserva() {
         Usuario.ingresarComoAdministrativo()
+
         Usuario.ingresaComoBibliotecario()
         .irAingresarDenuncia()
+
         .irReservaDeLibro()
         .completarDenuncia()
+
         .reservar()
 +
        .aceptar()
 +
        .verificarReserva()
 
         .aceptar()
 
         .aceptar()
        .verificarFinalizacionSinErrores()
 
 
         .salir();
 
         .salir();
     }
+
     }  
 
}
 
}
 +
</code>
 +
 +
De esta forma, escribimos el test de manera que al leer los métodos a los que se invoca, se entiende paso a paso lo que el test realiza, sin confundirnos con la implementación. Utilizando los patrones "method chaining" ,  "builder", "Page object" ayuda a realizar una abstracción de la implementación.
  
</code>
+
A continuación  las implementaciones de las clases utilizadas.
  
De esta forma, escribimos el test de manera que al leer los métodos a los que se invoca, se entiende paso a paso lo que el test realiza, sin confundirnos con la implementación. Utilizando los patrones “method chaining” y “builder” la implementación de la clase Usuario sería una cosa así:
+
==== Usuario ====
  
=== Usuario ===
 
 
<code java>
 
<code java>
package com.test.selenium.dsl;
+
package com.test.dsl;
  
 
import org.openqa.selenium.server.SeleniumServer;
 
import org.openqa.selenium.server.SeleniumServer;
  
 +
import com.test.dsl.pagina.HomeBibliotecario;
 +
import com.test.dsl.pagina.HomeUsuario;
 
import com.thoughtworks.selenium.DefaultSelenium;
 
import com.thoughtworks.selenium.DefaultSelenium;
 
import com.thoughtworks.selenium.Selenium;
 
import com.thoughtworks.selenium.Selenium;
import com.test.selenium.dsl.pagina.HomeAdministrativo;
 
  
 
public class Usuario {
 
public class Usuario {
   
+
 
 
     public static final String TIEMPO_ESPERA = "90000";
 
     public static final String TIEMPO_ESPERA = "90000";
 
     public static final String VELOCIDAD_CERO = "0";
 
     public static final String VELOCIDAD_CERO = "0";
Línea 64: Línea 77:
 
     private static SeleniumServer seleniumServer;
 
     private static SeleniumServer seleniumServer;
 
     private static Selenium selenium ;
 
     private static Selenium selenium ;
 +
 
      
 
      
 
     public static void inicializar() throws Exception {
 
     public static void inicializar() throws Exception {
 
         seleniumServer = new SeleniumServer();
 
         seleniumServer = new SeleniumServer();
 
         seleniumServer.start();
 
         seleniumServer.start();
         selenium = new DefaultSelenium( "localhost", 4444, "*chrome", "http://localhost:8585/");
+
         selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://localhost:8585/");
 
         selenium.start();
 
         selenium.start();
 
     }
 
     }
   
+
 
    public static HomeAdministrativo ingresarComoAdministrativo() {
 
        Usuario.login("administrativo");
 
        return new HomeAdministrativo(selenium);       
 
    }
 
   
 
 
     public static void finalizar() {
 
     public static void finalizar() {
 
         selenium.stop();
 
         selenium.stop();
 
         seleniumServer.stop();
 
         seleniumServer.stop();
 +
    }
 +
 +
    public static HomeBibliotecario ingresaComoBibliotecario() {
 +
        Usuario.login("bibliotecario_test","123456");
 +
        return new HomeBibliotecario(selenium);     
 
     }
 
     }
 
      
 
      
     private static void login(String username) {
+
     public static HomeUsuario ingresaComoUsuario() {
         selenium.setSpeed(VELOCIDAD_TEST);
+
         Usuario.login("usuario_test","123456");
 +
        return new HomeUsuario(selenium);      
 +
    }
 +
   
 +
    private static void login(String username, String password) {
 
         selenium.open("");
 
         selenium.open("");
 
         selenium.waitForPageToLoad(TIEMPO_ESPERA);
 
         selenium.waitForPageToLoad(TIEMPO_ESPERA);
        if (selenium.isElementPresent("relogin")) {
 
            selenium.click("relogin");
 
            selenium.waitForPageToLoad(TIEMPO_ESPERA);
 
        }
 
 
         selenium.type("username", username);
 
         selenium.type("username", username);
 +
        selenium.type("password", password);
 
         selenium.click("submitbutton");
 
         selenium.click("submitbutton");
 
         selenium.waitForPageToLoad(TIEMPO_ESPERA);
 
         selenium.waitForPageToLoad(TIEMPO_ESPERA);
        selenium.setSpeed(VELOCIDAD_CERO);
+
 
   
 
 
     }
 
     }
 
   
 
 
}
 
}
  
 
</code>
 
</code>
  
Como vemos en el ejemplo, el método ingresarComoAdministrativo instancia y devuelve un objeto de la clase HomeAdministrativo cuya implementación es:
+
Esta clase es la que determina con que rol vamos a probar la aplicación.
 +
El método '''ingresarComoBibliotecario''' instancia y devuelve un objeto de la clase '''HomeBibliotecario''' cuya implementación es:
  
=== HomeAdministrativo ===
+
==== HomeBibliotecario ====
 
<code java>
 
<code java>
package com.test.selenium.dsl.pagina;
+
package com.test.dsl.pagina;
 
 
import static org.junit.Assert.assertEquals;
 
  
 +
import com.test.dsl.Usuario;
 
import com.thoughtworks.selenium.Selenium;
 
import com.thoughtworks.selenium.Selenium;
import com.test.selenium.dsl.Usuario;
 
  
public class HomeAdministrativo {
+
public class HomeBibliotecario {
   
 
 
      
 
      
 
     private Selenium selenium;
 
     private Selenium selenium;
   
+
 
     public HomeAdministrativo(Selenium selenium) {
+
     public HomeBibliotecario(Selenium selenium) {
        this.selenium = selenium;
+
      this.selenium = selenium;
 
     }
 
     }
   
+
 
     public AltaDenuncia irAingresarDenuncia() {
+
     public ReservaDeLibro irReservaDeLibro() {
        selenium.setSpeed(Usuario.VELOCIDAD_TEST);
+
         selenium.click("link=ReservaLibro");
        selenium.getEval("this.browserbot.getCurrentWindow().selectview('applications')");
 
        selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
 
         selenium.click("link=IngresarDenuncia");
 
 
         selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
 
         selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
        selenium.setSpeed(Usuario.VELOCIDAD_CERO);
+
         return new ReservaDeLibro(selenium);
         return new AltaDenuncia(selenium);
 
 
     }
 
     }
   
+
 
   
 
 
     public void salir() {
 
     public void salir() {
         selenium.setSpeed(Usuario.VELOCIDAD_TEST);
+
         selenium.click("link=logout");      
        selenium.getEval("this.browserbot.getCurrentWindow().logoutMethod()");
+
    }
        selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
+
}
        if (selenium.isElementPresent("submit")) {
+
</code>
            selenium.click("submit");
+
 
            selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
+
Volvemos a utilizar el patrón builder y el método ingresar denuncia devuelve un objeto de la clase AltaDenuncia. De esta manera, cada clase representa a una pantalla de la aplicación, y expone cada funcionalidad que la misma ofrece. AltaDenuncia sería así:
        }
+
Volvemos a utilizar el patrón method chaining y el método '''irReservaDeLibro''' devuelve un objeto de la clase '''ReservaDeLibro'''. De esta manera, cada clase representa a una pantalla de la aplicación (page object), y expone cada funcionalidad que la misma ofrece (acciones que puedo realizar en la página y páginas a la que puedo acceder)
        selenium.setSpeed(Usuario.VELOCIDAD_CERO);
+
 
 +
==== ReservaDeLibro ==== 
 +
<code java>
 +
package com.test.dsl.pagina;
 +
 
 +
import com.test.dsl.Usuario;
 +
import com.thoughtworks.selenium.Selenium;
 +
 
 +
public class ReservaDeLibro {
 +
 
 +
    private Selenium selenium;
 +
 
 +
    public ReservaDeLibro(Selenium selenium) {
 +
      this.selenium = selenium;
 
     }
 
     }
  
     public HomeAdministrativo verificarFinalizacionSinErrores() {
+
     public ReservaDeLibro reservar() {
         assertEquals("IngresarDenuncia",     selenium.getText("link=IngresarDenuncia"));
+
         selenium.type("libro", "El señor de los anillos");
 +
        selenium.type("codSocio", "0001");
 +
        selenium.type("fechaHasta", "2011-01-01");
 
         return this;
 
         return this;
 
     }
 
     }
  
 +
    public ComprobanteDeLibro aceptar() {
 +
        selenium.click("submitbutton");
 +
        selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
 +
        return new ComprobanteDeLibro(selenium);
 +
    } 
 
}
 
}
 
 
</code>
 
</code>
  
Volvemos a utilizar el patrón builder y el método ingresar denuncia devuelve un objeto de la clase AltaDenuncia. De esta manera, cada clase representa a una pantalla de la aplicación, y expone cada funcionalidad que la misma ofrece. AltaDenuncia sería así:
+
==== ComprobanteDeLibro ==== 
 +
<code java>
 +
package com.test.dsl.pagina;
  
=== AltaDenuncia === 
+
import org.junit.Assert;
<code java>
 
package com.test.selenium.dsl.pagina;
 
  
 +
import com.test.dsl.Usuario;
 
import com.thoughtworks.selenium.Selenium;
 
import com.thoughtworks.selenium.Selenium;
import com.test.selenium.dsl.Usuario;
 
  
public class AltaDenuncia {
+
public class ComprobanteDeLibro {
   
 
   
 
 
     private Selenium selenium;
 
     private Selenium selenium;
   
+
 
     public AltaDenuncia(Selenium selenium) {
+
     public ComprobanteDeLibro(Selenium selenium) {
        this.selenium = selenium;
+
      this.selenium = selenium;
 
     }
 
     }
   
+
 
     public AltaDenuncia completarDenuncia() {
+
     public ComprobanteDeLibro verificarReserva() {
          
+
         Assert.assertTrue(selenium.isTextPresent("El señor de los anillos"));
         // Acá iría el código Selenium para completar los campos
+
         Assert.assertTrue(selenium.isTextPresent("0001"));
          
+
         Assert.assertTrue(selenium.isTextPresent("2011-01-01"));
 
         return this;
 
         return this;
 
     }
 
     }
 
      
 
      
     public HomeAdministrativo aceptar() {
+
     public HomeBibliotecario aceptar() {
       
+
         selenium.click("submitbutton");
        selenium.click("OK");
 
        selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
 
       
 
        return new HomeAdministrativo(selenium);
 
    }
 
   
 
    public HomeAdministrativo cancelar() {
 
         selenium.click("CANCELAR");
 
 
         selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
 
         selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
       
+
         return new HomeBibliotecario(selenium);
         return new HomeAdministrativo(selenium);
 
 
     }
 
     }
 
   
 
 
}
 
}
  
 
</code>
 
</code>
  
Tanto el cancelar como el aceptar, devuelven la pantalla anterior. Todo se maneja con objetos que representan cada pantalla.  
+
Tanto el aceptar (como el cancelar en ReservaDeLibro), devuelven la home del bibliotecario.  
  
 
== Conclusiones ==
 
== Conclusiones ==
  
 
El resultado de hacer los test de esta manera, es la facilidad de escritura y mantenimiento del test en si mismo. A su vez, se podría diseñar las clases a utilizar en el DSL a conveniencia.
 
El resultado de hacer los test de esta manera, es la facilidad de escritura y mantenimiento del test en si mismo. A su vez, se podría diseñar las clases a utilizar en el DSL a conveniencia.

Revisión del 12:30 29 abr 2011

Selenium es una herramienta para pruebas de aplicaciones web. Quién tuvo la posibilidad de utilizarla, sabrá lo poderosa que es la herramienta, la cuál tiene diversas funciones para ejecutar con nuestra aplicación web. Puede ocurrir, que nuestros test de Selenium se vayan complejizando a medida que nuestra aplicación crece, por lo que esta es una posible solución, combinando el poder de Selenium con un enfoque DSL.

Ejemplo

A continuación mostramos una propuesta de como encarar un test de Seleniumcon DSL. Para el ejemplo, suponemos un sistema de biblioteca, con las siguientes pantallas y su navegación. Se puede observar que pare este sistemas planteamos dos roles de usuario, el Bibliotecario y el Socio.


Creando un test

En el siguiente test, vamos a probar la funcionalidad de la reserva de libro. Para escribir el siguiente test, se utilizó una sintaxis acotada y simple con las siguientes reglas:

  • ir<NombreDeLaPagina>: Los métodos que comiencen con el prefijo “ir”, nos retorna el nuevo contexto (página).
  • verificar<Descripción>: Los métodos que comiencen con el prefijo "verificar", poseen los “assert” para comprobar alguna acción realizada en la página, este método siempre retona la misma página (this).
  • ingresarComo<Nombre del Rol>: Rol con el que se probará el sistema en la prueba.
  • aceptar, cancelar, volver, buscar: Son nombre de las acciones básica que se realizan en un página y pueden (o nó) devolver otra página.
  • <acciones propias de la página> : Estos métodos realizan acciones sobre el sistema sin ir a otra página. Ej.: reservar


ReservaSeleniumDslTest

package com.test.dsl;

import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test;


public class ReservaSeleniumDslTest {

   @BeforeClass
   public static void setUpClass() throws Exception {
       Usuario.inicializar();
   }

   @AfterClass
   public static void tearDownClass() throws Exception {
       Usuario.finalizar();
   }

   @Test
   public void comoBibliotecarioQuieroRealizarUnaReserva() {
       Usuario.ingresaComoBibliotecario()
       .irReservaDeLibro()
       .reservar()
       .aceptar()
       .verificarReserva()
       .aceptar()
       .salir();
   }    

}

De esta forma, escribimos el test de manera que al leer los métodos a los que se invoca, se entiende paso a paso lo que el test realiza, sin confundirnos con la implementación. Utilizando los patrones "method chaining" , "builder", "Page object" ayuda a realizar una abstracción de la implementación.

A continuación las implementaciones de las clases utilizadas.

Usuario

package com.test.dsl;

import org.openqa.selenium.server.SeleniumServer;

import com.test.dsl.pagina.HomeBibliotecario; import com.test.dsl.pagina.HomeUsuario; import com.thoughtworks.selenium.DefaultSelenium; import com.thoughtworks.selenium.Selenium;

public class Usuario {

   public static final String TIEMPO_ESPERA = "90000";
   public static final String VELOCIDAD_CERO = "0";
   public static final String VELOCIDAD_TEST = "350";
   
   private static SeleniumServer seleniumServer;
   private static Selenium selenium ;


   public static void inicializar() throws Exception {
       seleniumServer = new SeleniumServer();
       seleniumServer.start();
       selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://localhost:8585/");
       selenium.start();
   }
   public static void finalizar() {
       selenium.stop();
       seleniumServer.stop();
   }
   public static HomeBibliotecario ingresaComoBibliotecario() {
       Usuario.login("bibliotecario_test","123456");
       return new HomeBibliotecario(selenium);       
   }
   
   public static HomeUsuario ingresaComoUsuario() {
       Usuario.login("usuario_test","123456");
       return new HomeUsuario(selenium);       
   }
   
   private static void login(String username, String password) {
       selenium.open("");
       selenium.waitForPageToLoad(TIEMPO_ESPERA);
       selenium.type("username", username);
       selenium.type("password", password);
       selenium.click("submitbutton");
       selenium.waitForPageToLoad(TIEMPO_ESPERA);
   }

}

Esta clase es la que determina con que rol vamos a probar la aplicación. El método ingresarComoBibliotecario instancia y devuelve un objeto de la clase HomeBibliotecario cuya implementación es:

HomeBibliotecario

package com.test.dsl.pagina;

import com.test.dsl.Usuario; import com.thoughtworks.selenium.Selenium;

public class HomeBibliotecario {

   private Selenium selenium;
   public HomeBibliotecario(Selenium selenium) {
      this.selenium = selenium;
   }
   public ReservaDeLibro irReservaDeLibro() {
       selenium.click("link=ReservaLibro");
       selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
       return new ReservaDeLibro(selenium);
   }
   public void salir() {
       selenium.click("link=logout");        
   }

}

Volvemos a utilizar el patrón builder y el método ingresar denuncia devuelve un objeto de la clase AltaDenuncia. De esta manera, cada clase representa a una pantalla de la aplicación, y expone cada funcionalidad que la misma ofrece. AltaDenuncia sería así: Volvemos a utilizar el patrón method chaining y el método irReservaDeLibro devuelve un objeto de la clase ReservaDeLibro. De esta manera, cada clase representa a una pantalla de la aplicación (page object), y expone cada funcionalidad que la misma ofrece (acciones que puedo realizar en la página y páginas a la que puedo acceder)

ReservaDeLibro

package com.test.dsl.pagina;

import com.test.dsl.Usuario; import com.thoughtworks.selenium.Selenium;

public class ReservaDeLibro {

   private Selenium selenium;
   public ReservaDeLibro(Selenium selenium) {
      this.selenium = selenium;
   }
   public ReservaDeLibro reservar() {
       selenium.type("libro", "El señor de los anillos");
       selenium.type("codSocio", "0001");
       selenium.type("fechaHasta", "2011-01-01");
       return this;
   }
   public ComprobanteDeLibro aceptar() {
       selenium.click("submitbutton");
       selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
       return new ComprobanteDeLibro(selenium);
   }  

}

ComprobanteDeLibro

package com.test.dsl.pagina;

import org.junit.Assert;

import com.test.dsl.Usuario; import com.thoughtworks.selenium.Selenium;

public class ComprobanteDeLibro {

   private Selenium selenium;
   public ComprobanteDeLibro(Selenium selenium) {
      this.selenium = selenium;
   }
   public ComprobanteDeLibro verificarReserva() {
       Assert.assertTrue(selenium.isTextPresent("El señor de los anillos"));
       Assert.assertTrue(selenium.isTextPresent("0001"));
       Assert.assertTrue(selenium.isTextPresent("2011-01-01"));
       return this;
   }
   
   public HomeBibliotecario aceptar() {
       selenium.click("submitbutton");
       selenium.waitForPageToLoad(Usuario.TIEMPO_ESPERA);
       return new HomeBibliotecario(selenium);
   }

}

Tanto el aceptar (como el cancelar en ReservaDeLibro), devuelven la home del bibliotecario.

Conclusiones

El resultado de hacer los test de esta manera, es la facilidad de escritura y mantenimiento del test en si mismo. A su vez, se podría diseñar las clases a utilizar en el DSL a conveniencia.