Introducción
Llevo un tiempo investigando nuevas formas para definir y diseñar las pruebas en nuestro equipo. La idea principal es disponer de un modelo de diseño de pruebas que sirva como punto de encuentro entre el rol de producto, desarrollador y QE.
En este post vamos a hablar sobre Model-based testing o MBT que se nos ayuda a acercarnos bastante a la idea que he comentado arriba.
Y como siempre, vamos a verlo en práctica, para ello vamos a hacer uso de Graphwalker, una herramienta de MBT.
Podéis descargar el proyecto desde mi Github. Esta vez, veréis que el proyecto únicamente tiene logs para ver el flujo de secuencia del test. Usarlo como base y experimentad!
Model based testing (MBT)
En Model-based testing (MBT) se define nuestro SUT en base a vértices o vertex y la transición o edge entre estos.
El modelo, se compone de tres vértices o vertex:
- v_App_closed, como vértice inicial y final.
- v_App_running.
- v_Display_preference
De tal forma que en el caso de este modelo (que es sencillo) si queremos probar el 100% de los vértices y las transiciones, el test path más lógico sería:
Que para llegar de uno a otro requiere de ciertas transiciones o edge:
- e_Start_app, transición inicial.
- e_Close_app.
- e_Open_preferences.
- e_Close_preferences.
De tal forma que en el caso de este modelo (que es sencillo) si queremos probar el 100% de los vértices y las transiciones, el test path más lógico sería:
Como hemos dicho, nuestro caso es muy sencillo y es fácil identificarlo, pero lo normal es tener modelos mucho más complejos.
Para estos casos es cuando las herramientas de MBT como Graphwalker, nos ayudan a generar los test path.
Normalmente las herramientas MBT nos proporcionan dos formas de generar los test path.
OFFLINE
En la generación offline, los test path se generan previamente, de tal forma que puedan ser ejecutados con posterioridad por la herramienta.
ONLINE
En la generación online, los test path se generan automáticamente en tiempo de ejecución. Para este caso, la herramienta o el framework de MBT estará acoplado al código del test, pero esto nos aporta ciertas ventajas.
Vamos a verlo en la práctica!
Añadir Graphwalker a nuestro proyecto
Para añadir Graphwalker a nuestro proyecto, únicamente necesitamos añadir las dependencias y el plugin:
Lo veremos más adelante, pero mediante maven generamos las interfaces que tendrán que implementar nuestras clases.
Diseñando el modelo
Para diseñar el modelo vamos a usar yEd, que nos permitirá diseñar el modelo gráficamente, siendo totalmente compatible con Graphwalker.
Imaginemos que tenemos una aplicación, de venta de entradas. Formamos parte del equipo de pagos y tenemos una arquitectura basada en microservicios.
La idea es probar la feature de la compra de una entrada a través de las APIs.
Nuestro modelo se va a componer de cinco vertex:
Crear usuario -> Añadirle el medio de pago -> Crear el carrito de compra del usuario -> Añadir la entrada al carrito de compra -> Comprar el ticket
Si nos fijamos en los edges entre los vértices, podemos ver pueden ir acompañados de “cierta lógica”.
Es muy importante nombrar los edged, con una “e” por delante y los vertex con una “v”, de esta forma Graphwalker podrá identificarlos.
Por ejemplo, en el caso del edge e_AddPaymentMethod, vemos el código:
/paymentMethod=’paypal’; validPaymentMethod=true;
Esto quiere decir que Graphwalker, cuando vea el símbolo /, todo lo que venga posteriormente lo va a interpretar como código javascript, es decir, en esta transición vamos a setear valor para dos variables.
Si nos fijamos en el siguiente edge, vamos a ver un código un poco diferente:
[validPaymentMethod]
Graphwalker cuando vea los símbolos […], lo va a interpretar como un condicional, es decir, va a comprobar que el valor de validPaymentMethod sea true.
Al ser código javascript, tanto el código como las condiciones pueden ser mucho más complejas, depende de las necesidades de nuestras pruebas.
Os dejo un enlace de la documentación de Graphwalker donde podreis verlo con mucho más detalle.
GENERANDO LA INTERFAZ DEL TEST
Para generar la interfaz del modelo para nuestras pruebas, vamos a guardar el fichero graphml de yEd en el directorio src/test/resources/ del proyecto.
Ejecutamos el comando de maven:
mvn graphwalker:generate-test-source
Esto nos generará en el directorio target la interfaz que tendremos que implementar en nuestras pruebas para lanzarlo con Graphwalker:
Desarrollando y ejecutando las pruebas
En el test definiremos el algoritmo con el que queremos que se genere la secuencia de nuestro test. La interfaz nos indicará los métodos de los que se compone nuestro test.
En el test definiremos el algoritmo con el que queremos que se genere la secuencia de nuestro test. En nuestro caso:
@GraphWalker(value = "quick_random(edge_coverage(100))", start = "e_CreateUser")
- quick_random: Ejecutará el camino más corto, lo va decidir mediante el algoritmo de Dijkstra.
- edge_coverage: Que ejecute el 100% de los edges que hemos definido en el modelo.
Es decir, en nuestro caso va a ejecutar todos los edges, y por cada vuelta va calcular cual es el camino más rápido.
Esta parte es la más interesante e importante, debido a que depende el algoritmo de ejecución que definamos probaremos más o menos, es decir, decide nuestra estrategia de testing.
Os dejo un enlace donde podéis ver las diferentes opciones de ejecución.
Para ejecutar las pruebas:
mvn graphwalker:test
Como resultado vemos que se han ejecutado el 100 de los vertex y los edges, por lo que hemos lanzado pruebas con una cobertura del 100%.
Conclusiones
Definir las pruebas con MDT puede ayudarnos a alinearnos mejor con producto y con desarrollo, dado que los modelos clarifican bastante el flujo de testing de nuestro SUT.
El poder integrarlo con herramientas como Graphwalker en donde podemos definir el algoritmo de ejecución de las pruebas, nos aporta una gran flexibilidad a nuestra estrategia de testing.
Imaginemos que queremos hacer smoke testing, bastaría con definir la ejecución como:
e_start(reach_vertex())
Y si por ejemplo queremos hacer pruebas de estabilidad podemos añadirle tiempo de duración a nuestras pruebas:
random(time_duration())
Otro de los puntos fuertes, es que no está casado con ningún tipo de testing, es decir, al definir únicamente el flujo o los pasos del test, podemos realizar desde test de UI mediante selenium, a APIs, performance etc.