lunes, 16 de agosto de 2010

Test de unidad con Selenium

Hace algunos días que estoy automatizando un conjunto de Smoke Tests, es decir tests que se ejecutan para ver si algo se rompe al hacerlo, sin verificar que el resultado de las operaciones sea el esperado. Estos tests se ejecutaban antes a mano, con el esperable desperdicio de recursos.

Para la automatización decidí usar Selenium, por ser una de las herramienta open-source más populares y porque tiene muy buena portabilidad entre browsers. Selenium es una librería Javascript que se agrega a nuestra aplicación Web (en forma transparente, ya vamos a ver como) y que la extiende con una interfaz capaz de recibir comandos que permiten interactuar con los elementos gráficos de la UI.

Existen varias maneras de utilizar Selenium. La más sencilla es Selenium IDE, que permite crear tests mediante el proceso de "Record & Play", es decir que tiene la capacidad de registrar nuestra interacción con un sitio web y de reproducir luego exactamente las mismas acciones. Esta modalidad no exige saber nada sobre el funcionamiento de Selenium y permite que una persona sin conocimientos de programación pueda escribir sus propios tests. Sin embargo tiene el grave incoveniente de que los tests así creados son muy frágiles: cualquier cambio en la aplicación causa que los tests dejen de funcionar y deben ser creados de nuevo desde cero (este problema no es particular de Selenium IDE si no que es compartido por todas las herramientas que utilizan el esquema "Record & Play").

Debido a estas limitaciones decidí utilizar Selenium Remote Control. Esta solución tiene una arquitectura de cliente/servidor. El servidor se encarga de controlar una instancia de un Web Browser (puede ser Internet Explorer, Firefox, Opera, Chrome y Safari). El cliente interpreta los tests y envía comandos al server, que se ocupa de ejecutar los comandos recibidos en el browser que controla. Esta arquitectura permite al servidor injectar las librerías Javascript que componen el core de Selenium en forma transaparente, sin que tengamos que instalarlas en el servidor de la aplicación que queremos testear como en versiones anteriores.

Como la interfaz entre el cliente y el servidor es HTTP puro, los clientes (y por lo tanto los tests) pueden estar escritos en cualquier lenguaje capaz de ejecutar un request HTTP. Existen clientes para C#, Java, Perl, PHP, Python y Ruby.

Veamos un ejemplo sencillo de como utilizar Selenium RC con Ruby. En primer lugar es necesario realizar la instalación de Selenium. Para el server, solo es necesario bajar y descomprimir este archivo y tener disponible un run-time de Java.

Para instalar el cliente ruby, podemos utilizar el siguiente comando:

gem install selenium-client

Una vez realizada la instalación, hay que poner a correr el server:

cd selenium-server-1.0.3 java -jar selenium-server.jar

Y podemos probar este pequeño script de prueba, que ejecuta una búsqueda en Google:

#hello_google.rb require "selenium/client"

begin @browser = Selenium::Client::Driver.new \ :host => "localhost", :port => 4444, :browser => "*firefox", :url => "http://www.google.com", :timeout_in_second => 60

@browser.start_new_browser_session @browser.open "/" @browser.type "q", "Selenium seleniumhq.org" @browser.click "btnG", :wait_for => :page puts @browser.text?("seleniumhq.org") ensure @browser.close_current_browser_session end

Este script crea una instancia del cliente Ruby y ejecuta los comandos necesarios para escribir la frase "Selenium seleniumhq.org" en el cuadro de texto de google y a continuación presiona el boton de búsqueda.

El código que se ocupa de escribir el texto es un ejemplo muy representativo de como funciona Selenium asi que vamos a utilizarlo para entrar un poco más en detalle.

@browser.type "q", "Selenium seleniumhq.org"

El segundo parámetro del método type es solo el texto a cargar y por lo tanto no es muy interesante. El primer parámetro en cambio es el mecanismo para localizar el objeto en que queremos escribir (locator en la jerga Selenium) y es muy importante, ya que la identificación en forma efectiva y robusta de los distintos componentes de la interfaz es fundamental en estos trabajos de automatización.

En este caso el locator es simplemente el id del text box. Esta forma de identificación es claramente la más sencilla y probablemente la mejor desde el punto de vista de la mantenibilidad. Sin embargo, muchas veces no es posible utilizarla, ya sea por que los objetos que queremos identificar no tienen id o porque el id de un mismo objeto cambia entre sesiones. Para estos casos, Selenium tiene formas más poderosas de identificacion.

Por ejemplo podemos identificar objetos utilizando XPath (http://en.wikipedia.org/wiki/XPath), que es un lenguaje para acceder a las distintas partes de un documento escrito en un lenguaje de markup. Veamos algunos ejemplos de selectores XPATH

Escribe en el elemento de tipo input con el id "q" (equivalente al ejemplo anterior) @browser.type "xpath=//input[@id='q']", "texto a buscar"

Identifica un div con el texto "Hola", pero hace click en otro div, que es "hermano" del identificado @browser.click "xpath=//span[text()='Sales']/../div"

XPATH es muy poderoso y un tutorial completo escapa a los objetivos de este artículo. Pueden encontrar una introducción aquí: http://www.w3schools.com/xpath/xpath_intro.asp . Un detalle importante para la utilización de XPath con Selenium es que no todos los browsers proveen una buena implementación de XPath, por lo que muchas veces conviene pedirle a Selenium que utilice su propia versión de la herramienta, que es un poco más lenta pero más poderosa.

Otra alternativa para identificar objetos con Selenium es CSS. En este caso la forma de nombrar los objetos es exactamente la que utilizamos para describir su presentación en archivos CSS. Veamos algunos ejemplos:

Escribe en el elemento de tipo input con el id "q" (equivalente al primer ejemplo XPath) @browser.type "css=input#q", "texto a buscar"

Click en el link que contenga el texto "Mi Link" @browser.click "css=a:contains('Mi Link')

Una ventaja importante de CSS sobre XPath es que en Internet Explorer corren muchísimo más rápido los comandos que utilizan CSS.

Como conclusión, Selenium me pareció una herramienta muy interesante. Con un poco de práctica se pueden escribir tests con bastante facilidad y esos tests se pueden utilizar después en distintos browsers prácticamente sin modificaciones. En artículos posteriores vamos a hablar de buenas prácticas en la escritura de tests con Selenium y también de la utilización de algunas herramientas para hacer más fácil la detección de formas de identificar los componentes de la UI con los que queremos interactuar.

No hay comentarios:

Publicar un comentario