Banear

domingo, 24 de febrero de 2013

Pruebas de Regresión con Selenium y Wicket con Internet Explorer y Firefox

Introducción


Selenium nos permite, entre otras cosas, grabar pruebas realizadas sobre una aplicación web (las acciones que hacemos desde el navegador) para reproducirlas y repetirlas en un futuro sin necesidad de intervención del usuario (o tester).

Ventajas

  • Una vez grabado el caso de prueba, si queremos repetirlo, simplemente lo reproducimos desde Selenium. Si por ejemplo un caso de prueba nos implica hacer 15 clicks por pantallas y tardamos N minutos, si lo grabamos con Selenium, no tendremos que repetir el proceso otra vez mecánicamente como si fuésemos "monos", ya se encarga Selenium de reproducirlo las veces que queramos.
  • Lo podemos utilizar con otros fines. Por ejemplo, para preparar una demostración en la que queremos pasar por N pantallas, para que no se nos olvide nada (o simplemente como guión).
  • Una vez grabados todos los casos de prueba, podemos reproducirlos de golpe para comprobar que tras nuevos cambios que hayamos hecho no se ha estropeado nada (efectos colaterales sobre otras pantallas). Obviamente, habrá que actualizar los casos de prueba (realizando una nueva grabación del caso de prueba o eliminado/insertando pasos) si hemos introducido o eliminado componentes en las pantallas.

Integración de Selenium



El botón de la izquierda es para reproducir los tests y el de la derecha para grabar las interacciones con el navegador (las pruebas que hagamos).
Hacemos un par de clicks sobre la aplicación web con la que estemos trabajando, y grabamos la prueba:

 

Integración de Selenium con Wicket (Consideraciones "especiales")

Si reprodujésemos el test, tendríamos problemas al trabajar con Wicket, ya que si no se especifica en cada componente el MarkupId, Wicket lo autogenera (diferente en cada nueva reproducción de la prueba, por lo que en no encontraría el componente por variar su id en cada plan de ejecución).
Para subsanar este problema, hacemos lo siguiente:
  • En la clase que tengamos que extienda de WebApplication, sobrescribimos el método init, añadiendo la línea getDebugSettings().setOutputComponentPath(true), que nos sirve para habilitar el atributo"wicketpath" (identificador único).

@Override

protected void init() {

   super.init();

   getDebugSettings().setOutputComponentPath(true);

}
  • Creamos una carpeta "wicketPathLocatorBuilder" (Donde queramos, en D: o donde sea
  • Creamos un fichero "user-extension.js.wicketPathLocatorBuilder" con el siguiente contenido, que guardamos en esa carpeta:
 LocatorBuilders.add('wicketpath', function(e) {
        this.log.debug("wicketpath: e=" + e);
        if (e.attributes && e.hasAttribute("wicketpath")) {
            this.log.info("found attribute " + e.getAttribute("wicketpath"));
            return "//" + this.xpathHtmlElement(e.nodeName.toLowerCase()) + "[@wicketpath=" + this.attributeValue(e.getAttribute("wicketpath")) + "]";
        }
        return null;
    });
LocatorBuilders.order.unshift(LocatorBuilders.order.pop());

  • Vamos a Selenium y seleccionamos la opción Options > Options
  • Indicamos la ruta en "Selenium Core extensions"

  • Aceptamos y reiniciamos Firefox
Ya podemos grabar y reproducir nuestras pruebas con Selenium en Wicket sin preocuparnos de que genere ids distintos de cada vez:


Exportando los casos de prueba a scripts

Utilizando "Export Test Case As...":

Podemos exportar los casos de prueba como "scripts" para lanzarlos con Junit, Ruby, C#, ...
Por ejemplo, con Ruby:


Y el script sería este (al ejecutarlo nos abre automáticamente el Firefox, lanza la prueba y cuando acaba se cierra y pone los resultados):

require "selenium-webdriver"
gem "test-unit"
require "test/unit"

class Papidal < Test::Unit::TestCase

  def setup
    @driver = Selenium::WebDriver.for :firefox
    #@base_url = "http://localhost:8090/"
    @accept_next_alert = true
    @driver.manage.timeouts.implicit_wait = 30
    @verification_errors = []
  end
 
  def teardown
    @driver.quit
    assert_equal [], @verification_errors
  end
 
  def test_papidal
    @driver.get("http://localhost:8090/myproject/")
    @driver.find_element(:xpath, "//a[@wicketpath='showModalLink']").click
  end
 
  def element_present?(how, what)
    @driver.find_element(how, what)
    true
  rescue Selenium::WebDriver::Error::NoSuchElementError
    false
  end
 
  def verify(&blk)
    yield
  rescue Test::Unit::AssertionFailedError => ex
    @verification_errors << ex
  end
 
  def close_alert_and_get_its_text(how, what)
    alert = @driver.switch_to().alert()
    if (@accept_next_alert) then
      alert.accept()
    else
      alert.dismiss()
    end
    alert.text
  ensure
    @accept_next_alert = true
  end
end

Si queremos lanzarlo con Internet Explorer, simplemente nos aseguramos que el zoom del navegador esté al 100% (o nos dará un error del estilo "browser zoom level was set to 104%. It should be set to 100%"). Para ello ajustamos el IE pulsando Ctrl+Scroll (menuda frikada :)).

Además, en el caso de ruby con IE, simplemente sería cambiar el Driver, poniendo esta línea:
@driver = Selenium::WebDriver.for :ie

Y añadiendo al PATH (variable de entorno) el fichero: IEDriverServer.exe, que descargaremos de una de estas dos  rutas:
Download version 2.30.2 for (recommended) 32 bit Windows IE or 64 bit Windows IE (http://code.google.com/p/selenium/downloads/detail?name=IEDriverServer_x64_2.30.2.zip)

Como en casa tengo un equipo de 64 bits, me bajé la segunda opción.

El resultado al lanzar el script, sería la apertura automática del IE ejecutando los tests y al acabar se cerraría sólo mostrando por consola esta traza:

Exportando los casos de prueba de Selenium con Junit

Si preferimos exportar el caso de prueba con Junit, para poder lanzarlo cómodamente desde Eclipse, necesitaremos algunas dependencias adicionales. Según el navegador que deseemos usar, modificaremos a nuestro gusto la dependencia "selenium-firefox-driver", en este caso, nos permitirá lanzar nuestros tests contra Firefox:

<dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.8.2</version>

</dependency>

<dependency>

      <groupId>org.seleniumhq.selenium</groupId>

      <artifactId>selenium-java</artifactId>

      <version>2.29.1</version>

</dependency>

  

  <dependency>

      <groupId>org.seleniumhq.selenium</groupId>

      <artifactId>selenium-firefox-driver</artifactId>

      <version>2.29.1</version>

</dependency>

Además, para evitar otros errores al ejecutar el test, necesitaremos la siguiente versión de "xml-apis":

<dependency>

     <groupId>xml-apis</groupId>

     <artifactId>xml-apis</artifactId>

     <version>1.4.01</version>

</dependency> 

Selenium, provee los drivers para dar soporte con Safari, IE, Firefox, … Con lo que podemos comprobar si nuestras pruebas funcionan en los distintos navegadores (compatibilidad). En este ejemplo me centro en Firefox, pero en teoría sólo hay que cambiar la clase del driver del navegador que queremos que ejecute el test.

Con JUNIT, con el servidor en local levantado en el puerto 8080, al lanzar el test se abrirá automáticamente el Firefox y en él se ejecutará el caso de prueba (navegando él sólo por las pantallas que le hemos indicado). Al finalizar, si no hubo errores, se cierra el navegador sólo y tendremos el test en "verde".



Para lanzar los Junit contra IE (acordaros de descargar el IEDriverServer.exe):

File file = new File("D:\\util\\IEDriverServer.exe");//El path que sea
System.setProperty("webdriver.ie.driver", file.getAbsolutePath());
driver = new InternetExplorerDriver();

Otras consideraciones

En ocasiones, el "wicketpath" no es suficiente, y para algún elemento puede ser necesario setear los siguientes atributos:

botonFiltro.setMarkupId("filtro1");
botonFiltro.setOutputMarkupId(true);

Se aconseja encarecidamente configurar el orden de los localizadores en el plugin de Selenium de la siguiente forma (según necesidades):



Si es posible que tarde unos segundos una pantalla hasta que se cargue, le damos tiempo a que se cargue la página, con la función isElementPresent. Cuando nos devuelva true, sabemos que ya está cargada la pantalla con el elemento que queremos usar para la prueba. Ejemplo:

//...

driver.findElement(By.xpath("//div[@id='bigPageLinks']/ul/li[3]/form/a")).click();
    for (int second = 0;; second++) {
         if (second >= 60) fail("timeout");
         try { if (isElementPresent(By.cssSelector("#modelo"))) break; } catch (Exception e) {}
         Thread.sleep(1000);
    }

    driver.findElement(By.cssSelector("#modelo")).clear();
    driver.findElement(By.cssSelector("#modelo")).sendKeys("8032");

//...

martes, 12 de febrero de 2013

USOSME WEB: Probando el mundo de los PaaS

Hace unos meses, aprovechando el tiempo libre para aprender un poquillo sobre el Cloud Computing interactúar un poco con las PaaS y esas cosas, empecé a montar una aplicación web (idealmente tendría su imagen para android) hasta que empecé a tener menos tiempo o cambiaron mis prioridades.

Al final quedó en esto: http://usosmeweb.appspot.com

Voy a dividir esta esta entrada en dos: Para qué podría valer (tal y como está actualmente) y cómo está estructurada técnicamente.

Para qué vale

Puedes loguearte en usosmeweb a través de tu cuenta de Facebook (ten en cuenta que te logueas sobre la plataforma de Facebook, así que yo no voy a conocer tú contraseña, simplemente Facebook me dirá si te has logueado correctamente o no. De hecho, le pido la mínima información al servicio) o si lo prefieres, proporcionando un nombre de usuario y una cuenta de correo a la que enviaré un correo de confirmación de la cuenta.



Una vez logueado, podrás acceder a las opciones del menú. Actualmente están activas las siguientes:
  • Alertas
    • Públicas: Puedes ver los avisos que han creado los usuarios. Pulsando el botón "Detalle" puedes ver los comentarios asociados o añadir nuevos, como si fuese un foro. Podrías crear un aviso para informar sobre algún evento o simplemente para hacer una quedada para ir en bici :). Recomiendo encarecidamente no escribir datos especialmente sensibles.
    •  Crear/Revisar: Desde aquí podrás crear nuevas alertas o revisar las que has creado. Para crear una alerta, simplemente debes decribirla, seleccionar el nivel de urgencia, el tipo de actividad y la privacidad (si sólo quieres que la vean tus amigos, colegas, tu familia o si es pública). También podrás "cancelar" una alerta que hayas creado. Cuando la canceles aparecerá tachada.
    • Recibidas: Desde aquí podrás consultar las alertas "privadas". Es decir, si eres amigo de otro usuario y ese usuario ha creado una alerta que sólo pueden verla sus amigos, tú la verás, pero no será pública.
  • Mi Gente
    • Podrás decidir y ver a quién sigues y quien te sigue. Al estilo Facebook, pero mucho más simplificado. Tú decides que grupo de gente puede ver tus alertas.
  • Mi cuenta
    • Configuración: Para cambiar tu avatar/imagen de usuario. Sólo permito imágenes pequeñas, porque la cuota que me facilitan no es demasiado grande :)


Tardará en cargar un ratillo si el servidor tiene que "levantar" la instancia. He metido algún error para comprobar la redirección a la página genérica de errores. Como actualmente no la estoy manteniendo, en cuanto Google vuelva a cambiar librerías de GAE, fallará estrepitosamente y en ese momento eliminaré el enlace.

Estructura técnica

- PaaS: Google App Engine
- Cliente: JSF (PrimeFaces), CSS, HTML, JS
- Servidor: Spring, DataNucleus, JDO
- Construcción: Eclipse, Maven, Plugin para GAE
- Cacheo en servidor de imágenes y páginas estáticas
- Integración con API de Facebook para autenticación
- Control de versiones: Mercurial

Después de investigar otro poco las plataformas que ofrecían servidores, bases de datos y otros servicios en la nube y tras probar Openshift y Google App Engine (con los que espero sacar una comparativa algún día), posteriormente he montado, en un día malo de estos que vienen en verano, una aplicación con la siguiente arquitectura para CloudBees:

Cliente: Wicket y HTML5
Servidor: JEE5 (en esta versión los EJBs y todo lo demás tiene mejor pinta y es un calco de Spring)
Base de datos: MySQL
Construcción: Maven
Servidor de aplicaciones: JBoss 7.1

Tengo los cimientos montados (aunque no son visibles) y simplemente he dejado una pequeña chorrada para probar el DnD (Drag And Drop) de HTML5. Lo cierto es que tiene bastante buena pinta, aunque como siempre, mientras no paguemos, nuestras pruebas estarán bastante limitadas, en este caso por la cuota de espacio de BD que nos proporciona CloudBees, por ejemplo.