Selenium y QA Automation: PageObjects
En los artículos anteriores, “Selenium y QA Automation” y “Selenium y QA Automation: Tests” hemos visto cómo utilizar Selenium/Webdriver.
En este caso vamos a ver cómo organizar estos tests. Realmente no es necesario el conocimiento previo de Selenium, ya que puede utilizarse algún otro framework. Sin embargo, sí que resulta interesante ya que lo usaremos para los ejemplos.
Aunque los PageObjects se idearon para pruebas en la Web (de hecho, creo que son una idea de Selenium), este artículo está escrito pensando que los PageObjects pueden usarse tanto en aplicaciones de escritorio como web, por lo que veréis “pantalla” o “página” indistintamente.
Creando tests
Cuando uno comienza a escribir tests, comienza haciendo las cosas mal. Es normal. Para hacer las cosas bien hechas es necesario adquirir experiencia.
Lo primero que suele hacerse es escribir toda la inicialización de un test en el propio test. Poco a poco, uno comienza a darse cuenta de que no hace más que repetirse, así que decide pasar al siguiente nivel: crear funciones dentro del propio Test Case, de manera que pueda reutilizarlas.
Sin embargo, se sigue repitiendo código entre distintas Suites. Así que se aumenta al siguiente nivel: Crear archivos aparte que permitan reutilizar toda esta estructura.
PageObjects
Los PageObjects son el siguiente paso. Consisten en encapsular la funcionalidad de nuestra aplicación en objetos. Dicho de otro modo: Consisten en modelar de nuevo nuestra aplicación, de manera que cada pantalla sea un objeto (un PageObject) y cada acción que pueda realizar el usuario sea un método.
Serán los PageObjects quienes interactúen con la aplicación principal, aislando este uso de los tests y consiguiendo así tests más robustos.
Ejemplo
Y llegados a este punto, vamos con un ejemplo completo. Consiste en un formulario que, al pulsar el botón, reemplaza el propio formulario por un saludo:
|
|
Y ahora vamos con los tests:
|
|
Es un poquito largo, pero vamos a analizarlo para ver que no es tan complicado.
Por una parte, tenemos la clase BrowserTest
, que ya analizamos en el artículo anterior. Como vimos, en esta clase se enmascara el acceso principal a Selenium. Vemos que está configurado para utilizar el servidor. Podéis modificarlo para que utilice webdriver.Firefox
en lugar de webdriver.Remote
si queréis simplificarlo, pero con este cambio estaréis perdiendo versatilidad. De esto hablaré más en otro artículo.
A continuación creamos un PageObject
básico, que lo único que implementa es el constructor. Con el fin de poder acceder a Selenium, necesitamos el driver. La idea es, justamente, usar el driver exclusivamente desde los PageObjects.
Lo siguiente es el PageObject propiamente dicho, en la clase GreetingsPageObject
, en la que definimos las acciones que se pueden realizar en la página:
writeName
: escribe el nombre en el lugar adecuado.submit
: envía los datos.getContent
: Obtiene el contenido final.
Y finalmente el test en la clase AutomationTest
, que define claramente lo que se desea hacer.
Me gustaría hacer incapié en varias cosas que podéis observar en este ejemplo:
- No se hacen comprobaciones en el PageObject.
- No se accede directamente a Selenium/Webdriver desde el test. Únicamente desde el PageObject.
- Estamos creando el objeto directamente desde el Test, pero sería mejor utilizar una Factory.
Problemas
Los PageObjects no están exentos de problemas, pero ya hay quien se ha peleado con éstos y ha aportado algunas soluciones:
Enlaces a otras pantallas
Es normal que, al pulsar un botón, aparezca otra pantalla/página.
La forma de modelar esto es mediante la devolución de otro PageObject que modele la página destino.
|
|
NOTA: Se supone que ambas clases heredan de los PageObjects del ejemplo, por lo que necesitan el browser en su constructor.
Distintos resultados para una misma acción
En ocasiones podemos tener distintos resultados para una misma acción, dependiendo del estado interno. Por ejemplo, en la ventana de Login, el resultado será diferente si el login es válido o inválido.
La solución a este problema es utilizar distintos métodos que modelen cada una de estas posibilidades:
|
|
Comprobaciones
Todas las comprobaciones deberían hacerse en el propio Test y no en el PageObject. De esta manera se aíslan los Tests del manejo de la aplicación. No deberían realizarse comprobaciones en los PageObjects, ya que esto rome la encapsulación.
Existe una excepción. Es un patrón bastante corriente comprobar que estamos en la página que debemos estar al crear un PageObject. Esto es normal, ya que encontrarnos en una página incorrecta puede dar lugar a errores más difíciles de encontrar.
Por ello, suelen utilizarse los constructores para realizar este tipo de comprobaciones. Hay que evitar realizar estas comprobaciones en otros sitios. Sin embargo, debe tratarse de realizar comprobaciones rápidas, ya que se realizarán muy a menudo.
Páginas muy grandes
En ocasiones vamos a encontrar páginas muy grandes, que suelen dividirse en objetos más pequeños y, a menudo, reutilizados en distintas páginas.
En estos casos debemos crear PageWidgets, que embeban la funcionalidad de todo el componente. Así resultará más sencillo reutilizarlos y organizarlos.
|
|
Ventajas
- Si usamos un framework diferente de Selenium/Webdriver, es suficiente cambiar la implementación de los PageObjects, ya que el funcionamiento de las páginas debería ser el mismo. Los tests seguirán siendo igual de válidos.
- Es más fácil reutilizar la funcionalidad.
- Los tests son independientes del acceso a la Web.
- Los tests se quedan más sencillos.
Referencias
Lo mejor es irse a la propia web de selenium, donde está el artículo sobre Page Objects. Es un artículo bastante conciso, en el que se proponen ejemplos en Java.