Table of Contents
Los informes de ejecución de las pruebas de regresión demuestran su importancia y el valor que aportan al producto, más aún si están automatizadas y se ejecutan continuamente. Un plan de regresión se compone de todas las pruebas destinadas a validar las implementaciones y los flujos de negocio de forma continua, a medida que la aplicación crezca también lo hará el plan de regresión, mientras que otros planes como smoke que comprueba los principales flujos de negocio se mantendrán sin cambios mientras existan estos flujos.
Para tener un correcto proceso a nivel de pruebas, se deben realizar primero las correspondientes pruebas de las funcionalidades a nivel de código a través de Pruebas Unitarias y de API tests, mientras que las pruebas de negocio (smoke, regresión) se realizarán en una fase posterior para seguir aumentando el valor de las pruebas realizadas sobre la aplicación.
Sin embargo, en muchas ocasiones el alto valor de las pruebas de regresión viene acompañado de un alto coste en tiempo de mantenimiento. Toda la automatización de QA se ha enfrentado a muchas horas y días de trabajo escarbando en los logs de las ejecuciones de las pruebas e investigando el código intentando averiguar si el error de la prueba se debe a un posible bug, a un error de programación o a falsos negativos y positivos. En este artículo desarrollamos las principales pautas para tratar de corregir las causas que dificultan el mantenimiento de nuestro plan de regresión.
(Re)considerar la estrategia de automatización de pruebas
Una de las principales razones por las que generalmente nos cuesta mantener la regresión es porque nuestras pruebas tienden a realizar demasiadas acciones y comprobaciones al mismo tiempo que tienden a recorrer los flujos de usuario de nuestra aplicación, muchas de ellas innecesariamente. Esta clase de pruebas llamadas end-to-end tests tienden a ser por definición difíciles de mantener, lentas y frágiles. Representan sobre todo un agujero negro de tiempo en el mantenimiento de la regresión.a
Minimizar el número de pruebas e2e y maximizar el número de pruebas funcionales debería ser siempre el primer punto de partida de cualquier suite de regresión.
Los <a href=»https://en.wikipedia.org/wiki/Functional_testing»>Functional tests</a> (feature tests) son el antónimo de e2e, nos permiten verificar que una implementación o característica de la aplicación funciona, simple y llanamente. Dado que una aplicación está llena de cientos de pequeñas implementaciones, probar cada una de ellas por separado siempre garantizará la cobertura, el mantenimiento y los resultados fiables sobre el estado de la aplicación.
Consideremos el siguiente ejemplo:
Escenario: El usuario descarga una factura
Given que el usuario entra en la aplicación
And el usuario crea una nueva factura
And el usuario abre la factura creada
When el usuario pulsa la opción de descarga
Then, se descarga la factura
Este es un ejemplo de una «prueba de fallos». El objetivo de esta prueba es comprobar la funcionalidad de ‘Descarga’ de la aplicación, para lo cual es necesaria una factura como condición previa. ¿Es necesario crear una nueva factura para descargarla? ¿Qué ocurre si la implementación de crear factura falla? En este caso, no sólo fallaría otra posible prueba de crear una factura, sino que esta prueba fallaría específicamente porque no se ha podido crear la factura, cuando en este caso crear la factura no es el objetivo de la prueba sino descargarla. Debemos intentar reducir al máximo todas aquellas precondiciones que sean innecesarias para lo que queremos comprobar.
Por lo tanto, el ejemplo anterior podría ajustarse de la siguiente manera:
Escenario: El usuario descarga una factura
Given que el usuario entra en la aplicación
And el usuario abre la factura ‘X1023’
When el usuario pulsa la opción de descarga
Then, se descarga la factura
Dado que en nuestro hipotético escenario sigue siendo necesario abrir la factura para descargarla, debemos abrirla antes de pulsar la opción, pero en este caso la factura ya está en el sistema en el que ejecutamos la prueba, por lo que minimizamos la posibilidad de que una condición previa provoque un fallo en nuestra prueba.
Apoya tus pruebas con llamadas a la API en su beneficio
Otra posibilidad que tiende a olvidarse es la de utilizar llamadas a la API para apoyar nuestros tests de UI, en otras ocasiones directamente se evitan bajo el argumento de «utilizar tests de UI fieles a la experiencia de usuario». Obviamente las pruebas de UI deben interactuar en la medida de lo posible tal y como lo haría un usuario real, pero hay que tener cuidado a la hora de manejar las precondiciones ya que son una fuente potencial de errores no deseados que comprometen la validación real de nuestra prueba. Además, en nuestra suite de regresión ya habría pruebas cuya validación final ya comprobará (fiel a la experiencia del usuario) lo que serían precondiciones en otras pruebas.
Apoyarnos en las llamadas a la API para nuestros tests de UI no sólo hará que la ejecución de los mismos sea mucho más rápida, sino que además no estaremos alterando el comportamiento de la aplicación, ya que detrás de cada formulario que interactúe con la parte del Backend siempre se estará haciendo una llamada desde un endpoint pasándole cierta información. Podemos utilizar directamente el mismo endpoint enviándole la información que enviaríamos a través del formulario.
Supongamos el siguiente escenario: Para verificar la cuenta de un usuario en el sistema, la cuenta debe ser nueva, por lo que no podemos tener una cuenta de usuario almacenada en el sistema como en el ejemplo anterior.
Escenario: El usuario verifica una cuenta creada
Given el usuario abre el formulario de inscripción
And el usuario envía el formulario de inscripción
And el usuario entra en la aplicación con el usuario creado
When el usuario verifica la cuenta
Then el usuario ve un mensaje de verificación completada
Para crear una nueva cuenta, el usuario debe acceder al formulario de registro, rellenar todos los campos del formulario y enviar la información para crear el nuevo usuario. Conociendo la información que enviamos a través del endpoint, podemos definir el test de la siguiente manera:
Escenario: El usuario verifica una cuenta creada
Given que se crea una cuenta aleatoria con los siguientes datos
| field | value |
| firstname | John |
| lastname | Doe |
| email | [email protected] |
| password | Test123456 |
And el usuario entra en la aplicación con el usuario creado
When el usuario verifica la cuenta
Then el usuario ve un mensaje de verificación completada
El endpoint utilizado en el paso Given llamará al mismo endpoint que se utiliza en el formulario de registro, y la información utilizada es la misma que se enviaría a través del formulario. Al final del paso se obtendría el correo electrónico generado con el que el usuario podría acceder al sistema.
Aunque la implementación de esta llamada a la API en el framework de automatización puede parecer compleja a primera vista, los beneficios de utilizar la API de la aplicación para las pruebas de UI en soporte superan ampliamente la inversión inicial de preparar el framework con esta capacidad. Además, por otro lado, también permite la posibilidad de realizar pruebas de backend en el mismo framework, algo que sin duda es realmente beneficioso para garantizar el correcto funcionamiento de la API del sistema.
Habla con los desarrolladores para una estrategia de localización coherente: ID sobre Xpath
Sin duda, la comunicación entre el equipo de control de calidad y el de desarrollo es clave a la hora de conseguir pruebas fiables y robustas. Entre las diferentes formas de seleccionar los elementos de la interfaz de usuario de una aplicación, destacan sobre todo las siguientes:
- ID
- Xpath
Los IDs son identificadores únicos de los elementos UI de la aplicación. Si se definen correctamente, los ID son nombres inmutables, fácilmente accesibles y utilizados por las principales herramientas de prueba de software, como Selenium. En cambio, requieren una definición y un mantenimiento explícitos por parte del equipo de desarrollo.
Xpath (XML Path Language) es un lenguaje que permite construir expresiones que recorren y procesan un documento XML. Los elementos del árbol DOM de una aplicación se construyen como un documento XML y herramientas como Selenium nos permiten seleccionar los elementos de la aplicación con esa estructura, sin necesidad de la intervención explícita del equipo de desarrollo y permitiéndonos automatizar rápidamente la prueba a través de esos elementos.
Sin embargo, el uso de Xpath también tiene sus inconvenientes. Mirando el ejemplo de abajo:
<form>
<div>
<span>Email</span>
<input type="text">
</div>
<div>
<span>Password</span>
<input type="password">
</div>
<div>
<input type="checkbox"><span>I agree to the <a href="https://www.test.com/terms" target="_blank">privacy policy</a>of this website.</span>
<div>
<div>
<input type="checkbox"><span>I want to subscribe to the newsletter.</span>
<div>
<button type="submit">Sign up</button>
</form>
<form>
<div>
<span>Email</span>
<input type="text">
</div>
<div>
<span>Password</span>
<input type="password">
</div>
<div>
<input type="checkbox"><span>I agree to the <a href="https://www.test.com/terms" target="_blank">privacy policy</a>of this website.</span>
<div>
<div>
<input type="checkbox"><span>I want to subscribe to the newsletter.</span>
<div>
<button type="submit">Sign up</button>
</form>
Teniendo esta estructura, nuestros selectores podrían obtenerse a través de XPath de esta manera:
@FindBy(xpath = "//span[text() = 'Email']/input")
public static WebElement email;
@FindBy(xpath = "//span[text() = 'Password']/input")
public static WebElement password;
@FindBy(xpath = "//input[@type = 'checkbox'][last() - 1]")
public static WebElement termsCheckbox;
@FindBy(xpath = "//input[@type = 'checkbox'][last()]")
public static WebElement subsCheckbox;
@FindBy(xpath = "//button[text() = 'Sign up']")
public static WebElement submitButton;
Aunque los selectores son funcionales, los localizadores pueden ser fácilmente «rotos» por cualquier cambio introducido en la estructura del formulario, ya sea cambiando los componentes que dan lugar a la estructura span > input, añadiendo nuevos elementos como casillas de verificación o cambiando el texto del formulario en el ‘front’ de la app. Cualquier cambio hará que la prueba falle y añadirá tiempo de mantenimiento ajustando los selectores.
<form>
<div>
<span>Email</span>
<input type="text" id="email">
</div>
<div>
<span>Password</span>
<input type="password" id="password">
</div>
<div>
<input type="checkbox" id="terms"><span>I agree to the <a href="https://www.test.com/terms" target="_blank">privacy policy</a>of this website.</span>
<div>
<div>
<input type="checkbox" id="newsletter"><span>I want to subscribe to the newsletter.</span>
<div>
<button type="submit" id="submit">Sign up</button>
</form>
<form>
<div>
<span>Email</span>
<input type="text" id="email">
</div>
<div>
<span>Password</span>
<input type="password" id="password">
</div>
<div>
<input type="checkbox" id="terms"><span>I agree to the <a href="https://www.test.com/terms" target="_blank">privacy policy</a>of this website.</span>
<div>
<div>
<input type="checkbox" id="newsletter"><span>I want to subscribe to the newsletter.</span>
<div>
<button type="submit" id="submit">Sign up</button>
</form>
@FindBy(id = "email")
public static WebElement email;
@FindBy(id = "password")
public static WebElement password;
@FindBy(id = "terms")
public static WebElement termsCheckbox;
@FindBy(id = "newsletter")
public static WebElement subsCheckbox;
@FindBy(id = "submit")
public static WebElement submitButton;
Gracias a los identificadores introducidos por el equipo de desarrollo, los selectores son ahora mucho más robustos y sólidos, inquebrantables ante cualquier cambio en función del diseño, ya sea editando los elementos del formulario o añadiendo otros nuevos.
Debemos pedir al equipo de desarrollo que defina identificadores (ID) para todos aquellos elementos con los que interactuamos en nuestras pruebas.
Utiliza un entorno y un conjunto de datos específicos para realizar sus pruebas
Por último, pero no por ello menos importante, cabe destacar las enormes ventajas de utilizar un entorno dedicado a las ejecuciones de pruebas de regresión, además de trabajar con un conjunto de datos específico para ellas. Trabajar con estos elementos proporciona enormes beneficios:
Entorno dedicado
- Consistent performances when testing in a controlled environment.
- Un entorno dedicado le permite aislar el código y verificar el comportamiento de la aplicación mientras se asegura de que ninguna otra actividad o fuente interfiera con los resultados.
- Posibilidad de representar fielmente un entorno de producción para realizar las pruebas, lo que proporciona seguridad sobre la realidad de los resultados obtenidos.
Dataset
- Control de los datos utilizados en las pruebas para cada ejecución.
- Disponer de un conjunto de datos evita que las pruebas tengan que crear datos para ejecutar sus validaciones, lo que reduce la posibilidad de que los resultados se alteren en caso de error durante la creación de los datos de la prueba.
Estos elementos constituyen el punto de partida sobre el que debe ejecutarse cualquier conjunto de pruebas de regresión. Además, tanto el entorno como el conjunto de datos pueden desplegarse mediante tareas automatizadas en servicios como Jenkins o Gitlab, lo que permitiría tener un estado «limpio» antes de cada ejecución de las pruebas de regresión.
Conclusiones
El mantenimiento de la regresión es esencial para verificar el estado del producto, sin embargo es una tarea que fácilmente puede ser muy costosa en términos de tiempo dedicado al QA, reduciendo la capacidad en otras tareas como la definición/refinamiento de nuevas pruebas o la automatización. de nuevas pruebas que a su vez se añadirían al plan de regresión. Por ello, debemos llevar a cabo buenas prácticas que nos ayuden a limitar el tiempo invertido en el mantenimiento de la regresión.
Author
-
QA Automation with over 5 years experience working in both manual and automation testing, on web and mobile testing. Passionate about AI/ML & testing processes.
Ver todas las entradas