Trucos para añadir pruebas de performance con Taurus a tu pipeline de Integración Continua

Hace tiempo que trabajo en testing de performance, y ya varios años que me ha tocado participar en proyectos donde se implementa Continuous Integration y Continuous Delivery. Tuve la suerte de poder participar en un proyecto para un cliente con una realidad bastante exigente en cuanto a requerimientos de performance, y donde implementaban CI/CD incluyendo pruebas de performance en su pipeline. En particular algo bien interesante en esto, es que uno no está ejecutando pruebas de carga en cada nuevo build, no estamos simulando la carga esperada del sistema, sino que a las pruebas de performance en el pipeline le ponemos otro foco. El foco en este caso está en detectar degradaciones de performance lo antes posible. Apuntamos a feedback inmediato y a bajo costo (ejecutar una prueba de carga que simule la realidad esperada es cara ya que se requiere contar con infraestructura similar a la de producción y de acceso exclusivo, lo cual no sería fácil tenerlo para cada momento en que se quiera hacer un build en un enfoque de CI/CD). ¿Cómo logramos esto entonces? pues ejecutando pruebas más puntuales, fijas, de menor carga, pero que siempre hagan una misma validación, revisando que los resultados no tengan desviaciones que hagan que se “pierda” performance.

 

Para protocolos abiertos como HTTP/S existen cientos de herramientas. Una de las más utilizadas es JMeter, ya que cuenta con una gran popularidad, la comunidad Apache le da respaldo, y además de HTTP soporta una gran variedad de protocolos. El hecho de que herramientas como BlazeMeter, Visual Studio Team Services, Soasta, y tantas más, de las que ejecutan las pruebas de performance desde el Cloud, la utilicen como formato de prueba, convierte a JMeter en el estándar de facto para las pruebas de performance. Desde mi punto de vista el punto flojo de esta herramienta es que el lenguaje que brinda para automatizar es un lenguaje gráfico, lo cual no es lo más deseado para los programadores. Además, como este lenguaje gráfico se guarda en un formato xml, esto no lo hace CI/CD-friendly, ya que si quiero ver los cambios entre una versión y otra, mirar las diferencias del xml no va a resultar nada práctico.

 

Es por esto que herramientas como Gatling son de mi preferencia para este tipo de pruebas integradas a nuestro motor de integración continua, ya que las pruebas se programan en código (en este caso, en el lenguaje Scala). Otra opción, y creo que más alineada al desarrollador moderno que le gusta programar en un editor de texto minimalista y de fondo negro, es utilizar Taurus. Taurus es una herramienta opensource desarrollada por Blazemeter, que permite especificar las pruebas de performance en formato yml, en archivos de textos bien simples (¡en este mismo blog Aritz ya ha hablado de Taurus!). Esta herramienta solo sirve para especificar el script, y luego para ejecutarlo lo que hace es convertirlo a un script JMeter (o a alguna de las 8 o 9 herramientas con las que es compatible), logrando recolectar los resultados y generando una interfaz gráfica liviana (a diferencia de JMeter) para ver los resultados en vivo, así como también enviando los datos a BlazeMeter para poder contar con un reporte HTML en vivo, el cual realmente queda muy bien (y para usarlo no necesitamos pagar nada, es más, ni siquiera necesitamos tener una cuenta en BlazeMeter).

 

Al trabajar con Taurus, en este enfoque de CI/CD, hay un “truco” que a mi ver es bien fácil de implementar y que da muy buen resultado. Básicamente, la idea es dividir todo el archivo yml de Taurus en tres archivos:

  • Uno con el flujo que estamos simulando, o sea, el caso de prueba, la secuencia de requests.
  • Otro para la especificación del escenario, o sea, qué carga queremos simular, cuántos threads, etc.
  • Y por último, uno donde indiquemos cuáles son los criterios de aceptación.

 

Aquí puedes ver un ejemplo:

Así tendremos tres archivos que nos permitirán ejecutar la prueba con el siguiente comando

bzt runner.yml criteria.yml load.yml

En este proyecto en Github tengo un ejemplo súper básico, pero que muestra esta idea de dividir los scripts, y puedes ver cómo organizarlos: https://github.com/fltoledo/taurus_simplest_example

 

Lo que nos permite esto es lo siguiente:

  • Facilitar la lectura, entendimiento y mantenibilidad de las pruebas.
  • Utilizar el mismo script (flujo, caso de prueba) en distintos ambientes, donde quizá lo que cambia es la carga que le quiero ejecutar, y así también los criterios de aceptación.

Con respecto a este segundo punto, creo que es fundamental poder utilizar el mismo script de pruebas, y tener distintos archivos que especifique distintas cargas y criterios de aceptación. Por ejemplo, en el ambiente de test quiero ejecutar una carga bien chica y puntual, pero en staging me interesa ejecutar una carga mayor, ya que el hardware es más similar a producción, y así los criterios de aceptación serán más exigentes también.

Otro aspecto que considero que es importante, está relacionado a qué métricas definirán los criterios de aceptación. Acá lo que tengo para aportar es lo siguiente:

También será importante definir la carga a ejecutar, y esto lo dejé explicado en este post que te invito a leer aquí.

Por último, considero beneficioso tener el código de pruebas en el mismo repositorio de código que el sistema, en un folder específico, pero lo importante es que me facilite la trazabilidad entre las pruebas y lo probado.

 

Resumiendo (TL;DR), creo que Taurus es una excelente herramienta para integrar pruebas de performance a nuestro pipeline, obteniendo así feedback temprano sobre la calidad de los cambios que se van introduciendo al sistema, específicamente en cuanto a velocidad. Para lograrlo en forma efectiva, te compartí por aquí algunos trucos que he ido recolectando en estos últimos años en diversos proyectos, y me gustaría también que me compartas tus experiencias al respecto. Puedes hacerlo en este post, en mi blog en esta entrada, o me encuentras en Twitter acá. Si quieres saber más sobre mí puedes hacerlo leyendo aquí.

Pruebas de rendimiento y rastreo mediante Spring Cloud Sleuth y Zipkin

Introducción

Siguiendo por el hilo del post anterior en este artículo me gustaría tratar sobre el tracing de los servicios. Cuando hablamos de “tracear” los microservicios, estamos hablando de rastrear todas las llamadas y respuestas que se hacen entre los diferentes servicios de nuestro sistema.

Tener un control sobre cada llamada es muy importante ya que nos aporta una visibilidad en tiempo real del estado de cada petición y respuesta que se da, de tal forma que no perdemos ningún detalle de lo que ocurre (tipos de peticiones y respuestas, tiempos y errores) entre nuestros servicios.

En nuestro caso vamos a hacer las pruebas con zipkin como sistema de rastreo y para nuestros servicios vamos a hacer uso de Spring Cloud Sleuth que es la herramienta que nos aporta Spring Cloud para instrumentar sus servicios.

Podéis descargar el proyecto de pruebas como siempre desde mi GitHub.

He de decir que cuando desarrollé el proyecto de pruebas lo hice sobre la versión 1.3 de Sleuth, ahora se encuentran en su versión 2.0. Si queréis migrarlo os dejo el siguiente enlace.

No solo me gustaría centrarme en el uso de Zipkin o Sleuth, si no, que este post viene dado por un debate que tuve con mis compañeros de equipo (y que luego trasladé a Federico Toledo), sobre la mejor estrategia para probar el rendimiento de los servicios públicos e internos.

Entendamos públicos como aquellos que están abiertas al consumo exterior e internas como aquellas que solo son consumidos por otros servicios.

Mi opinión es que el mejor approach era realizar las pruebas de rendimiento sobre los servicios públicos, ya que estos internamente iban a llamar a los servicios internos, siempre y cuando tendríamos alguna forma clara de rastrear y obtener los datos de los tiempos y el rendimiento de los servicios internos.

Bien, ¿Y qué tal si usamos zipkin y sleuth y así vemos un ejemplo de esto mismo? Vamos a ver qué conclusiones sacamos.

De forma sencilla, como funciona Sleuth y Zipkin

Veamos un ejemplo del rastreo de una petición para ver y entender la información recogida. En la imagen de abajo vemos dos trazas. Las trazas están compuestas por spans (lo veremos ahora) y muestran la información del “recorrido” de la petición, es decir, lanzando una petición, porque servicios hemos pasado.

Traza zipkin
En este caso hemos lanzado una petición a purchase-service que internamente ha llamado a inventory y account-service. Si accedemos a la información de la traza, lo vemos con más detalle.

Lo que vamos a ver son los spans, es decir, la unidad de información que contiene los datos de una request/response, los tiempos etc. Vamos a tener un span por cada petición y respuesta que se haya dado dentro de la traza. Cada traza va a tener asociados una serie de spans, por lo que en nuestro caso los spans A, B y C estarán asociados a la traza T.

Zipkin spans

span info
Vamos a acceder al span de inventory-service para ver su información con más detalle:

Las anotaciones son los registros de los eventos de la petición. En este caso se puede ver los tiempos en los que el cliente ha realizado la petición (CS), esta se ha recibido (SR), se ha respondido (SS) y cuando el cliente ha obtenido la respuesta (CR).

Por otro lado, en la tabla inferior vemos información más detallada sobre la petición en cuestión. La key get-inventory-item y su valor se ha definido en la instrumentación del código, para aportar mayor información en el span.

Si hubiera algún error, veríamos los detalles del error en este punto:

zipkin error

Dado que creo que es un tema muy interesante, os recomiendo el siguiente enlace enlace a la documentación de sleuth, donde se profundiza mucho más en la terminología y en cómo trabaja con zipkin.

Entendiendo el proyecto de pruebas

 

microservice

El proyecto con el que vamos a trabajar consiste en tres servicios: Purchase, Inventory y Account.

Cuando llamemos a la API de purchase-service para comprar un item esta llamará a inventory-service y account-service para obtener los datos necesarios para la compra.

Vamos a instrumentar las APIs de los tres servicios y atacaremos a purchase-service, dado que internamente llamará a los otros servicios.

Si queréis investigar sobre el proyecto de pruebas, veréis que Inventory-service también expone una API que llama internamente a account-service para obtener información.

Preparando el proyecto

Lo primero que vamos a necesitar es instalar zipkin en nuestro equipo. Yo recomiendo hacer uso de docker e instalar la imagen de zipkin. Os dejo un enlace para hacerlo.

Ahora tenemos que configurar nuestro proyecto e instrumentar las APIs para rastrear las llamadas. Lo primero es añadir las dependencias necesarias en nuestro build.gradle (en nuestro caso haremos uso de gradle).

Vamos a añadir la configuración para conectar a nuestro zipkin local. Para ello en el fichero application.properties de src/main/resources añadimos las siguientes propiedades:

spring.zipkin.baseUrl: Url de nuestro zipkin.

spring.sleuth.sampler.percentage: La cantidad de peticiones que se rastrean. Por defecto es el 10% (0.1), como en nuestro caso no vamos a saturar el sistema ya que es un proyecto de pruebas vamos a rastrear el 100% de las peticiones.

sample.zipkin.enabled: Cuando lo tenemos a true, las trazas se envían a zipkin, en nuestro caso es lo que queremos. En caso contrario se enviarán a la consola de logs.

Vamos a ver un ejemplo básico de como instrumentamos la API de purchase. Vamos al controlador y al método purchase que será donde accedamos cuando llamemos a /purchase. Creamos un nuevo tag, añadiendo información que nos resulte interesante de rastrear.

Haremos lo mismo para inventory-service y para account-service:
https://github.com/aaguila/spring-cloud-sleuth-zipkin-example/blob/master/inventory-service/src/main/java/com/qajungle/controllers/InventoryController.java

https://github.com/aaguila/spring-cloud-sleuth-zipkin-example/blob/master/account-service/src/main/java/com/qajungle/controllers/AccountController.java

En este caso la información que metemos en el tag tiene menos sentido debido a que en la petición ya tenemos el id. Para la versión 2.0  la forma de gestionar los spans cambia, ya que ahora en vez de usar Sleuth hace uso de Brave. Podéis verlo en la guía de migración.

Con la configuración básica que hemos añadido al proyecto ya podríamos rastrear las peticiones. Vamos a ver qué resultado obtenemos.

Rastreando las pruebas de rendimiento

Lo primero que vamos a hacer es arrancar zipkin para que comience a rastrear las peticiones de nuestros servicios. Para ello basta con arrancar el docker y ejecutar el comando:


docker run -d -p 9411:9411 openzipkin/zipkin

Para nuestro ejemplo vamos a configurar nuestras pruebas en taurus ya que creo que es una herramienta que hemos usado con anterioridad en el blog. Vamos a crear un script sencillo para generar una carga durante cinco minutos de diez peticiones por segundo con cinco usuarios concurrentes que aumentaran cada minuto. Podéis verlo en el directorio de purchase-service src/test/resources/taurus-tests

Tras lanzar las pruebas vemos que las peticiones han ido correctamente, sin ningún error y con un tiempo de respuesta estable, tiene sentido debido a que no hemos generado mucha carga.

Purchase load test

Claro… esto como hemos comentado en la introducción, nos da el reporte de las llamadas a purchase-service, pero no podemos diferenciar que parte del tiempo de respuesta corresponde a que las llamadas a las APIs internas, o en caso de que hubiera un error en una API interna cual sería etc. Perdemos cierta visibilidad, y aquí es donde entra el rastreo de las peticiones, que nos dará mucha más información.

En las siguientes imágenes vemos dos peticiones a la API de purchase:

  • La primera se ha realizado al comienzo de la carga, cuando la carga era un usuario y una petición por segundo.
  • La segunda se ha realizado al final de la carga, cuando la carga eran cinco usuarios concurrentes y diez peticiones por segundo.

1s trace

9s traceMediante zipkin vemos de forma desglosada cuanto tiempo se lleva cada API, por lo que en caso de que el tiempo de respuesta sería muy alto o estaría fuera de los límites que hayamos previsto, podríamos ver si el problema está en alguna API interna. O en el caso de que haya algún error en la llamada podríamos tener más información.

Conclusiones

Instrumentar nuestros servicios webs y así poder rastrear las peticiones nos aporta información de gran interés. Ya no solo para controlar el estado de cada petición y respuesta, si no, que puede ser una gran herramienta para realizar pruebas de rendimiento si nuestro sistema se compone de servicios externos e internos.

En el caso de nuestro proyecto nos hemos ahorrado el realizar pruebas de rendimiento para dos servicios internos de forma independiente, pero en el caso de un sistema complejo basado en microservicios, el valor puede ser mucho mayor, ya que las pruebas son más realistas, nos ahorramos pruebas, tiempo etc.

Consumer-driven contract testing con Spring Cloud Contract

Introducción

Como hemos comentado en entradas anteriores, la arquitectura en microservicos cada vez está ganando más fuerza, y esto supone un reto tanto para el desarrollo como para el testing. En el post vamos a hablar sobre Consumer-driven contract (CDC) testing y cómo podemos verificar que los microservicios trabajan correctamente y según lo esperado entre ellos.

Para ello, como siempre vamos a trabajar con un proyecto de ejemplo usando Spring Cloud Contract, que podréis descargar del repositorio de GitHub.

Consumer-driven contract (CDC) testing

Conociendo la arquitectura en microservicios sabemos que el mayor reto consiste en comprobar y validar la comunicación entre los distintos servicios y para ello las estrategias más comunes son ejecutar pruebas end-to-end o crear mocks de los servicios y lanzar pruebas de integración sobre ellos.

MicroservicesCon las pruebas end-to-end vamos a comprobar la comunicación entre los servicios simulando una experiencia más cercana a el entorno de producción. Primero debemos tener levantado todo el entorno (servicios, BBDD) lo que es costoso y dificulta la trazabilidad. Por otro lado, es fácil que los problemas de comunicación hagan que nuestras pruebas se ralenticen más de lo normal o que fallen. Las dependencia de tantos componentes debilitan nuestras pruebas.

Como hemos visto en un post anterior, mockear un servicio nos elimina los problemas anteriores. Por el contrario, puede que las pruebas contra el mock sean satisfactorias, pero en el entorno de producción la aplicación falle debido a un mal diseño de los mocks.

 

 

cdc testingCon Spring Cloud Contract diseñaremos los contratos de los servicios. El contracto sencillamente es la definición del comportamiento de nuestro servicio en base a una request. Con esa definición se van a generar teses a nivel de proyecto para validar los contratos, así como, se publicará el Stub en nuestro repositorio maven contra el que lanzaremos las pruebas de los clientes del servicio (como vemos en la imagen). No obstante, lo vamos a ver ahora y quedará mucho más claro.

Entre los beneficios que hemos visto, utilizar CDC testing con Spring Cloud Contract nos aporta:

  • Poder desarrollar nuestro servicio mediante metodología ATDD.
  • Una documentación actualizada y “honesta” de las APIs que conforman nuestro sistema.
  • Disponer de un mayor control de los clientes que consumen nuestros servicios. Debido a que sabremos quien realiza las pruebas contra los Stubs publicados.
  • Los contratos publicados siempre actualizados, debido que en el proceso de publicación se encuentra integrado en el proceso de “build”.

Entendiendo el proyecto de pruebas

 

project_services

El proyecto con el que vamos a trabajar consiste en un sistema de dos servicios. Inventory-service y Account-service.

Account-service expone una API que devuelve los datos de los usuarios e inventory-service expone una API que devuelve el inventario de un usuario vendedor.

Inventory-service consume account-service para obtener los datos del usuario de los items que va a devolver cuando le llamen, por lo que vamos a lanzar las pruebas sobre el Stub publicado de account-service.

Inventory-service publica su Stub para que cualquier cliente futuro pueda ejecutar las pruebas contra él. Así que vuestra imaginación puede volar y ayudaros a construir un nuevo servicio siguiendo los pasos del CDC 😛

 

Preparando el proyecto

Antes de ponernos a definir los primeros contratos vamos a ver que dependencias necesitamos en nuestro proyecto. Los ejemplos los vamos a ver del proyecto de account-service.

Dentro del build.gradle vamos a definir también en que directorio vamos a alojar los contratos, así como donde se guardarán las clases base de tests que utilizará para generar los teses de verificación de los contratos.

Definiendo los contratos

Siguiendo con account-service vamos a ver el contracto que hemos definido para devolver los datos de un usuario en base a su id, en /src/test/resources/contracts/ShouldReturnAccountById.groovy

Los contratos tienen una semantica muy clara y diferenciada. Vemos como definimos tanto la request que esperamos como la response que devolvemos para esa request.

Con este contracto estamos definiendo que para cuando llamen al endpoint: /account/[0-9]+ vamos a responder con un estado 200 y con un json que contien esos campos y datos.

Vemos que la url tiene un regex, por lo que un ejemplo de una llamada que la cumpla sería: /account/12345.

Es un contrato sencillo, y con esto tanto desarrollador (para comenzar el desarrollo) como el cliente (para consumirlo) ya sabrían cual sería el endpoint, así como los datos que tienen que mandar y los que van a recibir.

Podéis ver toda la información del DSL de los contratos en el siguiente enlace.

Si seguimos la metodología ATDD, sabemos que deberíamos hacer un desarrollo mínimo para poder lanzar los teses y que fallen e ir refactorizando hasta que los teses no fallen.

Veamos el paso de generar los contratos y publicar el Stub. Lanzando el comando generateContractTests va a autogenerar los teses de validación del contracto que hemos hablado anteriormente.


➜ account-service git:(master) ./gradlew generateContractTests
Starting a Gradle Daemon (subsequent builds will be faster)
:copyContracts
:generateContractTests

Y cuando lanzamos el comando install publicará en nuestro maven local (.m2) el Stub. Veámoslo:


➜ account-service git:(master) ./gradlew clean install
:clean
:compileJava
:compileGroovy NO-SOURCE
:processResources NO-SOURCE
:classes
:jar
:copyContracts
:generateClientStubs
:verifierStubsJar
:install

Si accedéis a .m2/repository/com/qajungle/account-service/0.0.1-SNAPSHOT podréis ver el Stub:

account-service-0.0.1-SNAPSHOT-stubs.jar

Desarrollando y ejecutando los teses

El cliente (inventory-service) consume account-service para obtener los datos del usuario del inventario. Veamos la clase de servicio y el gateway sobre el que tendremos que desarrollar el test:

/src/main/java/com/qajungle/services/InventoryService.java

/src/main/java/com/qajungle/gateway/AccountGateway.java

Como vemos AccountGateway.java es el encargado de realizar la llamada al API de account-service. Por lo que en este punto es donde tendremos que atacar contra el Stub publicado por el servicio. Veamos el test:

/src/test/java/com/qajungle/services/InventoryServiceTest.java

Lo más importante en este punto es ver que estamos atacando al Stub del servicio mediante el tag:


@AutoConfigureStubRunner(ids = "com.qajungle:account-service:+:stubs:8082", workOffline = true)

Si lanzamos el test del proyecto veremos que todo ha ido correctamente:


➜ inventory-service git:(master) ./gradlew -Dtest.single=InventoryService test
:compileJava
:compileGroovy NO-SOURCE
:processResources
:classes
:copyContracts
:generateContractTests
:compileTestJava
:compileTestGroovy
:processTestResources
:testClasses
:test

BUILD SUCCESSFUL

Cambiemos el contracto en account-service para comprobar que lanza contra el Stub. Por ejemplo cambiendo el valor de un campo.


➜ inventory-service git:(master) ✗ ./gradlew -Dtest.single=InventoryService test
:compileJava UP-TO-DATE
:compileGroovy NO-SOURCE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:copyContracts
:generateContractTests
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test


com.qajungle.services.InventoryServiceTest > should_return_seller_information FAILED
org.junit.ComparisonFailure at InventoryServiceTest.java:28

org.junit.ComparisonFailure: expected:<“[aritz]”> but was:<“[fail]”>

El resultado es que el test fallará con un ComparisonFailure en este caso.

Conclusiones

El hacer uso de contract testing nos proporciona unos beneficios que no podemos encontrar en otras estrategias para el testing de servicios. Es muy importante sacar los valores que nos aporta ya no solo en no depender del entorno, de los teses E2E, si no de poder desarrollar mediante ATDD, disponer de una documentación fidedigna y llevar un mayor control de quien consume nuestros servicios.

A día de hoy solo he probado Spring Cloud Contract, pero me consta que existen otras herramientas como Pact, que espero probar en un futuro cercano. No obstante como habéis podido comprobar Spring Cloud Contract no es complejo de utilizar.

Charlando de testing con: Federico Toledo

Introducción

Si hay algo que me gusta y de lo que creo que aprendo es de hablar con los compañeros sobre el mundo del desarrollo y el testing. La mayor parte del conocimiento que tengo hoy en día es gracias a leer artículos y a discutir ideas.

Como creo que es mejor compartir, he decidido crear una nueva serie en el blog en la que colgaré “entrevistas” o “momentos de charla” sobre testing con otros compañeros. Mi idea es hablar sobre testing no solo con testers o QEs, si no con todos los roles implicados en el mundo del desarrollo. Conocer diferentes opiniones y puntos de vista que puedan hacer discutir y replantearme las cosas.

En el primer post de la serie “Charlando de testing con”  lo he decidido hacer con Federico Toledo, tras hablar varías veces con el y leer su libro creo que tiene una opinión muy interesante sobre el actual estado del testing, así como grandes ideas. No me gustaría adelantaros nada así que os presento la charla con Federico Toledo:

Entrevista

 

QA Jungle – Charlando de testing con Federico Toledo from QA Jungle on Vimeo.

Comentarios del libro: Introducción a las Pruebas de Sistemas de Información

Introducción

tapa libro
Hace unos días Federico Toledo escribió un nuevo post en su blog hablando sobre el libro que escribió en el 2014  “Introducción a las Pruebas de Sistemas de Información“. Os recomiendo que os descarguéis el libro gratuito en el mismo post.

En la siguiente lectura voy a tratar de comentar y hacer crítica de lo que me gusta y lo que me gustaría encontrar en el libro. Del porqué recomiendo el libro y en mi opinión a qué tipo de lector o profesional está orientado.

¡Vamos allá!

 

 

El autor

Para mí es muy importante saber quién es el autor de un libro, sobre todo si es técnico. La razón es que muchos libros técnicos comparten el mismo contenido, hablan de los mismos temas y tratan los mismos conocimientos.

¿Entonces donde se encuentra la diferencia? En el autor. El autor aporta su experiencia, sus conocimientos y su personalidad. Eso es lo que hay que buscar en los libros técnicos. Tenemos que mirar más allá de la técnica pura y dura, tenemos que analizar la experiencia del autor sobre el tema que trata. Si no, solo se quedará en un simple manual.

A Federico Toledo le conozco de hablar personalmente alguna vez con él, de leer sus artículos y seguir sus consejos. Tiene un CV y una experiencia envidiable, pero sin entrar a lo personal, es una persona que se centra mucho en la práctica, al menos en los artículos que he leído. Por lo que antes de comenzar a leer el libro me hacía una idea de cómo se iba a enfocar.

Enfoque del libro

Uno de los mayores problemas de los libros técnicos, es la densidad a la hora de leerlos. ¡La mayoría se hacen eternos!

En mi caso ocurre por varios motivos: El autor es muy monoto, el libro no aporta mayor información que otros, no encuentro en el libro lo prometido. Introducción a las Pruebas de Sistemas de Información no ha sido el caso.

He leído varios libros y artículos de testing de diferentes autores y muchos de ellos solo han sido copias de otros, algunos ni siquiera aportaban la experiencia propia. Con lo que al final pensaba que solo aportaban humo.

En el caso de Introducción a las Pruebas de Sistemas de Informacióha sido diferente. Tenía ganas de encontrar un libro que se centrará en la práctica, que me aportara técnicas y estrategias para mejorar mis skills de testing. Bien es cierto que no profundiza demasiado en algunos de los conceptos tratados, tampoco creo que sea ese su objetivo, ya que es un libro de introducción al mundo del testing.

Temas del libro

¿El libro cubre temas suficientemente atractivos para mí? La respuesta es sí.

Cuando leemos libros de testing, nos encontramos con muchos temas sobre cómo implementar el testing en la metodología X. Al final nos encontramos que se han tratado más temas de metodologías y gestión de equipos que de estrategias o técnicas de testing.

Introducción a las Pruebas de Sistemas de Información, en mi opinión toca los temas necesarios para entender y comenzar a aplicar las estrategias y técnicas de testing. Creo que el autor ha buscado ser claro y practico con los temas, y creo que lo ha conseguido.

El que haya una temática tan clara ayuda a que también sea un libro de consulta, me explico. Muchas veces lees un libro técnico y tocas mucha información en varios temas, como si de una novela técnica se tratase. En el caso de Introducción a las Pruebas de Sistemas de Información si necesitas información sobre pruebas de rendimiento, basta con el ir al tema “Pruebas de Performance” para encontrarte toda la información necesaria, sin necesidad de bucear por todo el libro.

Lo que me gusta y lo que me gustaría encontrar


LO QUE ME GUSTA

  • Es un libro muy práctico, aportando ejercicios, lo que ayuda a entender y a interiorizar los temas que se tratan.
  • Para apoyar los ejercicios y las prácticas aporta herramientas al lector. Si de verdad te interesa, no te vas a quedar con la miel en los labios, puedes practicas e incluso utilizarlas para tu día a día en el trabajo.
  • El autor expone casos cotidianos (fuera de toda técnica) y humanos para entender los ejemplos de testing. Esto es muy positivo sobre todo para las personas que comienzan o quieren entender en el mundo del testing.
  • Repasa muchas técnicas de testing y la opinión personal respecto a cada una, con lo que aparte de aportar conocimientos técnicos, también aporta personales. ¡Lo que yo busco en un libro!
  • Aporta diferentes artículos y opiniones respecto a varios temas que trata. Creo que el autor buscar que el lector saque su propia conclusión a través de varios puntos de vista, algo muy positivo ya que ayuda a que el lector pienso por si mismo.

LO QUE ME GUSTARÍA ENCONTRAR

  • Cuando el autor habla de las técnicas de testing, echo en falta tratar la teoría de la pirámide del testing . Durante el tiempo que llevo trabajando como ingeniero de calidad me he encontrado con pirámides invertidas, lo que es signo de no aplicar y desarrollar correctamente el testing. Echo en falta que se trate este tema, ya que creo que es necesario conocerlo y entenderlo para no acabar con suites inmanejables y poco eficientes. No obstante, Federico Toledo si lo ha tratado en la versión en inglés del libro :).
  • Una vez más echo de menos que se traten temas de estrategias y técnicas de prevención. Es cierto que hoy en día el mundo del testing y el QE está orientado a garantizar la calidad mediante diferentes técnicas de pruebas (funcionales, performance, automáticas, unitarias…). Pero creo que sería interesante concienciar en acto de la prevención.
    Estás mismas técnicas pueden ser usadas para prevenir la inserción de fallos y la regresión de calidad, más que buscarlos. Poco a poco se está invirtiendo más tiempo en investigar técnicas de prevención de bugs mediante algoritmos de IA etc.
    Lo más seguro que no sea el “scope” del libro, pero suelo echar en falta algún tema o comentario al respecto, que muestre que el mundo del QE intenta expandirse y evolucionar también hacia otros aspectos.

Conclusión

Tanto si quieres comenzar o entender en el mundo del testing, como si eres un experto y quieres afianzar tus conocimientos te recomiendo la lectura del libro.

En el primer caso vas a ganar los conocimientos suficientes para entender la importancia del testing y comenzar a desarrollar tus habilidades.

En el segundo caso puede que te encuentres que el autor no profundiza, pero hay que entender que no es el “scope” del libro. A pesar de ello vas a poder descubrir y repasar técnicas muy interesantes, así como ver otros puntos de vista que seguramente van a hacer que te lleves una grata sorpresa.

Rest API performance testing con Taurus

Introducción

En el post vamos a tratar paso a paso como comenzar a lanzar pruebas de rendimiento sobre Rest API con Taurus, ejecutar una prueba y analizar los resultados. Por si queréis realizar las pruebas vosotros también os proporciono el código que podréis descargar de GitHub.

Durante los años que he trabajado con pruebas de rendimiento he podido probar varias herramientas de pago y libres. Desde hace unos pocos meses suelo hablar con Federico Toledo, sobre el mundo del testing en general, pero sobre todo le suelo hacer consultas sobre pruebas de rendimiento.

Entre varias recomendaciones, salió Taurus. Taurus es una herramienta open source que nos permite automatizar de forma sencilla nuestras pruebas. Lo interesante de Taurus es que las pruebas se definen mediante YAML, lo que hace que los teses sean más legibles y fáciles de escribir.

Otra característica interesante de Taurus es que nos permite ejecutar pruebas de otras herramientas como JMeter, Gatling, Selenium, JUnit etc. Podéis encontrar la lista en el siguiente enlace.

Entorno de pruebas

Vamos a levantar un servidor Rest API sobre el que vamos a lanzar las pruebas de rendimiento, Json-server.

Lo ideal sería tener una máquina con el servidor y otra con la ejecución de la prueba, si esto no os es posible, o si quereís hacer todo más rápido con fines de aprender, está bien instalar todo junto. No perdamos de vista que al momento de ejecutar pruebas de verdad esto lo debeis separar, para que el sistema y la herramienta de generación de carga no compitan por recursos.

Por lo que si queréis experimentar conmigo, podéis ver como instalarlo en el enlace anterior. 🙂

Objetivo de las pruebas

  1. Aplicación
    Vamos a imaginarnos que tenemos una aplicación en la que almacenamos datos de los posts publicados. Disponemos de una API Rest para que los consumidores puedan listar los posts o buscar un posts concreto. Por otro lado internamente usamos la API para publicar nueva información.
  2. Carga estimada
    Tras un análisis inicial para saber el número de peticiones por minuto (througput) que esperamos en nuestra API se estima que en el peor de los casos puede ser de:
    – 1200 rpm (peticiones por minuto) y una concurrencia de 20 usuarios en el caso del listado de posts (GET: /posts)
    – 1 rpm y una concurrencia de 1 usuario en el caso de la publicación de posts (POST: /posts)
    Lo ideal sería que el tiempo de respuesta fuera inferior a 40 ms.
  3. Que probar
    Tras el análisis inicial debemos saber cual es nuestro objetivo real de las pruebas. Podemos realizar pruebas de rendimiento para ver el porqué la aplicación no responde en un tiempo aceptable, realizar pruebas periódicas para ver que el tiempo de respuesta no se degrada, hasta pruebas para ver el número de máquinas que hacen falta para soportar una carga esperada.
    En este caso vamos a comprobar que para la carga esperada nuestra aplicación responde en un tiempo de respuesta aceptable y sin errores, es decir load testing o pruebas de carga.

Definiendo los casos de prueba

Vamos a configurar la prueba con dos escenarios diferentes:

1.- Escenario de peticiones al listado de posts. 20 usuarios concurrentes con un throughput de 20 rps (en Taurus el throughput se define por segundos), de tal forma que 20 * 60 = 1200 rpm.

2.- Escenario de peticiones al listado de posts. 1 usuario concurrente con un throughput de 1 rps.

En ambos casos para poder ver si el tiempo de respuesta se degrada con el tiempo vamos a ejecutar la prueba durante 1 hora, con una rampa de usuarios de 5 minutos, es decir, cada minuto se añadirán 4 usuarios, de tal forma que cuando pasen los 5 minutos tendremos la concurrencia de 20 usuarios.

La configuración de la prueba es bastante legible, no obstante os dejo el siguiente enlace donde podéis ver la sintaxis de configuración.

Ejecutando las pruebas

En la configuración de las pruebas de carga los dos escenarios se encuentran en la misma configuración “load-api-testing.yml”. Vamos a ver como ejecutarlos.

Para ejecutar las pruebas de carga tenemos que ejecutar el siguiente comando dentro del directorio de las pruebas:

bzt load-api-testing.yml 

taurus execution

Taurus nos proporciona un visor por consola aportando la información básica del estado de la prueba en vivo.

El panel izquierdo de la imagen nos muestra las peticiones que se hacen sobre el servidor api rest que hemos levantado para realizar las pruebas.

Taurus mediante su configuración nos permite subir los resultados al visor de resultados de nuestra cuenta de Blazemeter. Os dejo el siguiente enlace para que veáis como se configura.

 

Para conseguir unos datos objetivos sería deseable ejecutar cada prueba un mínimo de tres veces, siempre y cuando dispongamos de tiempo para poder realizarlas. Esto es normal ya que en una única ejecución hemos podido encontrarnos con problemas externos que pueden desprestigiar los resultados.

Analizando los resultados

Como hemos comentado anteriormente Taurus nos permite subir los resultados al visor de nuestra cuenta de Blazemeter. Vamos a analizar los resultados de nuestra prueba centrandonos en los datos más importantes para el objetivo del post.

Reporte temporal

grafico resultados

A simple vista se ve que el tiempo de respuesta va aumentando con el tiempo, superando incluso el límite que habíamos definido como ideal, RT < 40ms. Esto puede ser debido a dos motivos:

  1. Con el tiempo el sistema se degrada debido a la concurrencia de 50 usuarios.
  2. Debido que metemos un nuevo dato cada segundo, la base de datos crece,  y por lo tanto el tiempo de respuesta aumenta.

Para descartar la primera opción se debería hacer una prueba sin añadir datos cada segundo a la base de datos, de esa forma veríamos si el tiempo de respuesta se mantiene en un rango aceptable.

No obstante si ese fuera el caso, a futuro ibamos a tener el mismo problema ya que en algún momento ibamos a tener ese número de datos en la base de datos, y llevaría a picos en el tiempo de respuesta, como podemos ver entre las 17:05 y 17:15.

Estado de las peticiones

grafico resultados

Hemos visto el gráfico de datos, esto nos ayuda a ver la estabilidad del entorno en las pruebas, ahora vamos a analizar los datos del tiempo de respuesta.

En muchas ocasiones valoramos los datos por la media (avg), en este caso la media del tiempo de respuesta (27ms). Si únicamente nos fijaramos en la media nuestra prueba sería exitosa, y es ese el caso?

En la pestaña de Request Stats nos dan los resultados de los percentiles. Los percentiles nos muestran una medida bajo la cual se encuentra un porcentaje de la muestra, es decir, en nuestro caso el p95 (95%) de la muestra se encuentra por debajo de 73 ms.

Como vemos los resultados no son lo que esperábamos, si lo que deseamos es que la mayoría de lo usuarios tengan un tiempo de respuesta menor que 40 ms. Dista bastante de la media de 27ms.

Os dejo un enlace muy interesante sobre las métricas para performance testing:

https://www.federico-toledo.com/promedio-desviacion-estandar-y-percentiles-metricas-para-testing-de-performance/

Errores

errores

Si volvemos a ver la gráfica del reporte temporal podemos ver que sobre las 16:45, cuando el entorno comienza a desestabilizarse, empezamos a tener picos de errores.

Si vamos a la pestaña de errores podemos ver el número de errores. En el caso de nuestras pruebas la mayoría de errores vienen debido a problemas de conexión o respuesta con el servidor, muestra de que no tolera la carga.

Conclusiones

A pesar de que disponemos de una gran cantidad de herramientas, Taurus nos permite definir las pruebas de forma muy sencilla y reutilizable. Nos aporta los datos necesarios para obtener unos resultados concretos.

No lo he comentado anteriormente pero Taurus se puede integrar de forma muy fácil a nuestro CI, os dejo el siguiente enlace.

Por otro lado, teniendo claro el objetivo de nuestras pruebas y con un buen análisis de los resultados podemos llegar a conclusiones acertadas.

Mutation testing – PIT nuestro gran amigo

Introducción

No siempre las pruebas unitarias que realizamos dan la cobertura suficiente a nuestro código, no nos engañemos. Allá por los años 70-80 se creo un nuevo concepto de testing denominado mutation testing. Consistía en modificar ciertas lineas de nuestro código, para posteriormente probar si en realidad fallaba o no.

futurama_mutantesEstos cambios en el código se denominan mutantesy como si de un juego se tratará, debemos matarlos, es decir, si los teses fallan, se dice que han matado a los mutantes. Cuantos más mutantes matemos, mejor.

En este post vamos a ver como se realiza mutation testing mediante PIT, una herramienta que se va a encargar de realizar las mutaciones y ejecutar nuestros teses unitarios para ver si logra matar todos los mutantes.

Para ello, he dejado en mi github el proyecto con el que he realizado las pruebas. Podéis descargarlo y jugar a matar mutantes 😉

El código es “refactorizable”, lo sé, pero lo que he intentado es disponer de código sencillo que nos permita jugar con las pruebas unitarias y las mutaciones, lo vamos a ver.

Mutación del código

Podéis ver todos los tipos de mutaciones que realiza PIT en el siguiente enlace, pero vamos a ver una mutación sencilla, para ayudarnos a entender mejor lo que son las mutaciones en código.

La mutación de limites condicionales, consiste en cambiar los operadores relacionales de nuestras sentencias. Así comprobaremos que nuestras condiciones están bien construidas. Lo vemos en la siguiente tabla:

Original conditional Mutated conditional
< <=
<= <
> >=
>= >

En nuestro código tenemos el siguiente condicional:

De forma que la mutación condicional que se hará será:

Cuando ejecutemos las pruebas de mutación, PIT se encargará de realizar todas las mutaciones. Por defecto se aplican las mutaciones básicas, pero si queremos ser más especificos o llegar un mayor nivel de mutación, PIT nos ofrece una lista de mutaciones que tendremos que activar por configuración.

Lanzando las pruebas de mutación

A pesar de que las pruebas de mutación nos ayudan a detectar errores en nuestro código, también son muy útiles para comprobar si nuestras pruebas unitarias son correctas o garantizan la cobertura necesaría, por lo que son un gran apoyo.

Veamos la lógica de nuestro código y las pruebas unitarias que garantizan que el código funcione correctamente. Para el producto que queremos comprar, se comprueba que haya la cantidad disponible y en caso correcto se decrementa en 1 el valor de la cantidad y se devuelve. Sencillo.

Nuestras pruebas unitarias comprobaran que se descuentan correctamente los productos y que no podemos comprar más productos de los que dispone la máquina.

Si ejecutamos la pruebas unitarias, nos darán un resultado verde, es decir, todo funcional correctamente! (Según nuestras pruebas)

Unit testing result

Ahora es el turno de lanzar nuestras pruebas de mutación. Para ello, nos situamos en la raiz del proyecto y ejecutamos:
➜ mutation-testing git:(master) ./gradlew pitest


pit coverage

Las pruebas generan un reporte que se almacena en {project}/build/reports/pitest/. Vamos a analizarlos.

En nuestro proyecto de pruebas tenemos dos clases en src/java. En este caso la clase ProductCode es un enum, por lo que vamos a necesitar cobertura de test.

La lógica se encuentra en la clase VendingMachine.java, que es la encargada de gestionar la tienda.

 

En nuestras pruebas unitarias pensabamos que podríamos cubrir todos los casos, pero PIT nos muestra que no es así. Vamos a ver la razón. Las lineas verdes indican que PIT ha matado a los mutantes, mientras que las lineas rojas nos indican que los mutantes han sobrevivido.
pit coverage

Al fijarnos en la lista de mutaciones, vemos que en las líneas 20 y 27 se han realizado dos mutaciones de límite condicional que no han pasado las pruebas.
pit coverage

Si nos fijamos en el código de nuestras pruebas unitarías, vemos que en el test buy_correct_quantity_of_products, no comprobamos en todos los casos que ocurriría sí:

this.pXQuantity = quantity

Vamos a añadir los casos al test a ver que nos dice PIT:

Con los cambios realizados, hemos conseguido matar a todos los mutantes, por lo que los teses han pasado.

new pit result

Conclusiones

Las pruebas unitarías tienen una gran importancía en nuestros desarrollos, si las apoyamos con pruebas de mutación conseguimos garantizar una mayor cobertura.

Con las pruebas de mutación no solo nos curamos en salud de que nuestro código funcione correctamente, si no que comprobamos que nuestras pruebas den la cobertura que esperamos.

Se escucha poco hablar sobre el “mutation testing”, pero bajo mi opinión es un buena práctica el meterlo en nuestra estrategía de calidad, ya que es un arma más para mejorar el alcance de nuestras pruebas.

Bibliografía

  1. http://pitest.org/
  2. https://www.computer.org/csdl/mags/co/1978/04/01646911.pdf
  3. http://antares.sip.ucm.es/tarot09/index_files/MutationTestingTAROT09.pdf

Geb best practices, el antes y el después

foto3En la Greach (conferencia sobre groovy y grails de España) pude asistir a una charla títulada “Geb best practices” dada por Marcin Erdmann. En el post me gustaría tratar varios temas de los que se habló, compararlo a como desarrollo yo los proyectos a día de hoy, aplicar las buenas prácticas y ver el resultado.

Antes de pasar a la práctica tengo que decir que los proyectos que hablo en el “antes” estaban realizados sobre Geb 0.13.0 y ahora sobre 1.1.

Pódeis descargaros el proyecto de ejemplo desde mi repositorio de github. En el proyecto podéis ver todos los ejemplos que tratamos en el post.

Uso del “at checks”

foto3En Geb trabajamos con el page object pattern. El at check” es una “closure” definida a nivel de page que nos permite comprobar que nos encontramos en la página adecuada. ¿Cómo? Pues básicamente dentro del “at check” podemos realizar comprobaciones a nivel de página.

 

 

 

Antes

En algunos de mis proyectos uso el “at check” en cada cambio de página, es decir, cada vez que navegaba a otra página con el “to page” volvía a hacer un “at page” pensando que era necesario para realizar un cambio de contexto de page. En muchos casos el “at checker” únicamente devolvía true, por lo que aunque no se encontrará en la página siempre iba a ser correcto.

Después

Uso el “at check” únicamente para comprobar que me encuentro en la página que quiero estar. No lo útilizo como “cambio de contexto” de página ya que no es necesario si hago el “to page”.

Conclusión

En mi caso, haría un uso responsable de “at check”, es decir, no es necesario comprobar todas las páginas, si vamos a realizar acciones en la página y no nos encontramos en ella, nos va a saltar que no se encuentra el componente con el que queremos interactuar. Pero por otro lado, si hacemos uso del “at” vamos a saber que falla porque no se encuentra en la página o la página no ha cargado, y no porque el componente no se encuentra.

En el ejemplo podeís ver que yo lo he usado ya que testeo la navegación entre páginas.

Navegar a páginas

foto1

Lo común es navegar entre páginas y realizar las acciones y las comprobaciones que sean necesarias en nuestras pruebas funcionales. No siempre la url de un página es estática (ej.: /author/), si no que en muchas ocasiones necesitamos navegar a una url compuesta por elementos dinámicos (ej. /author/[ID]).

 

 

Antes

Para poder generar las url dinámicamente en base a los datos de entrada de un test hoy en día hago uso del “builder pattern”.

Sin entrar al detalle de como se contrulle un builder en groovy y explicandolo de una forma sencilla, únicamente paso los datos necesarios para construir la url al builder y una vez generada la seteo a la propiedad url de la página. Posteriormente navego a la página.
Después

Marcin comentó en la charla dos formas de poder navegar a una página construyendo la url dinámicamente, veamoslas.

La primera es con el uso del método de página “convertToPath“. El método puede recibir los parámetros que sean necesarios para construir la url.

Cuando hagamos uso del “to Page” añadirá el path construido por el método a la url estática de la página.

El otro ejemplo es el uso del método de página “getPageUrl”. Un poco más complejo. En este caso es necesario instanciar la página con los parámetros que espera la url.

Debemos hacer el “to Page” pasandole la nueva instancia de la página con los parámetros necesarios de la url en el constructor.

Conclusión

El hacer uso de un builder hace que tengamos que gestionar la lógica de la construcción de la url para cada página, y si no son muy “estandares” no será una tarea fácil.

Por otro lado hacer uso de “convertToPath” y “getPageUrl” nos permitirá mantener la responsabilidad de la construcción de la url en la propia página.

“Trackear” las páginas

foto3

Cuando hablamos de “trackear” las páginas, hablamos de capturarlas en una variable y utilizarlar sus elementos. Esto nos va aportar ciertas ventajas que vamos a ver con los ejemplos.

Antes

En todos mis proyectos lo común es navegar a la página en la que quiero e interactuar con los elementos directamente, esto hace que si la funcionalidad requiere de mucha interacción llegue un momento en que me pierda y no sepa si estoy interactuando con el elemento de una página u otro. Lo cierto es que puede llegar a ser poco legible.

El caso que presento no es el mejor ya que interactuamos con pocos elementos, pero imaginaos que hay más elementos e incluso elementos de otras páginas con el mismo nombre.

Después

Una buena practica es “capturar” la página y referenciar a los elementos a través de la variable. Veamos.

Conclusión

Capturar la página nos va a ayudar con la legibilidad de nuestros teses. En pruebas pequeñas igual no lo vemos algo muy útil, pero lo cierto es que es de gran ayuda.

Módulos

foto2

El uso de módulos nos permite reutilizar la definición del contenido de las páginas a través de varías páginas. Pero nos aportan mucho más valoro ocultar estructuras de componentes o interacciones complejas de los teses.

 

 

Antes / Después

En mi caso no hacía mucho uso de los módulos debido a que pensaba que los módulos únicamente servian para reutilizar contenido de las páginas, y no he tenído muchas ocasiones de utilizarlo. Pero saber que nos aportan mucho más que la reutilización aporta mayor valor.

Veamos un ejemplo de uso los módulos.

Por supuesto en la charla se tocaron más conceptos y buenas prácticas que comentaremos más adelante.

 

API Mocking con Apiary y SoapUI

Introducción

En muchas ocasiones en las que desarrollamos aplicaciones es necesario consumir APIs externas a nuestra aplicación. Este hecho a veces implica que en nuestros entornos de desarrollo o de pruebas tengamos que usar un sandbox, apuntar al entorno de pruebas de la API o, en caso extremo, apuntar al entorno de producción de la API.

Pero vemos varios puntos en contra de su uso:

  • Dependencia de sus entornos. Cuando su entorno no esté disponible no podremos ejecutar nuestras pruebas ya que es probable que nos fallen.
  • Tiempo de ejecución de los teses. El que nuestros teses tengan que comunicarse con servicios externos hará que el tiempo de ejecución aumente. Podéis ver una charla interesante sobre la reducción de tiempo de teses.

Una alternativa es simular la respuesta que esperamos, es decir, mockear la API externa.

API mocking

Como hemos comentado anteriormente, cuando hablamos de mockear una API, estamos queriendo simular cada respuesta que nos devuelve en base a la petición que hagamos.

La ventaja de mockear la API es que no necesitamos depender de que el entorno esté disponible. Otra ventaja importante es que reduciremos el tiempo de las pruebas, obviamos cualquier lógica de la API, ya que devolvemos una respuesta directa a la petición. Si el servicio mock lo tenemos en local o en nuestra red interna, la comunicación será más rápida.

Como desventaja se encuentra el hecho de que no realizamos las pruebas contra un entorno “real”, por lo que ante cualquier cambio en la API, tendremos que actualizar nuestro mock siempre y cuando el cambio nos afecte.

Una de las tareas más importantes es la de definir bien los diferentes casos, es decir, los diferentes usos o peticiones que vamos a realizar a la API.

Vamos a ver dos herramientas (con sus ventajas y desventajas) con las que mockear una API.

APIARY

URL: https://apiary.io/

Apiary es una herramienta online, que mediante un editor sencillo nos permite diseñar la respuesta que queremos devolver a una petición concreta.

apiary editor

Veamos el ejemplo default de una petición GET:

Cuando hagamos una petición GET al path /questions nos devolverá una respuesta 200 con listado de preguntas en formato json.


Apiary nos proporciona un inspector de las peticiones que se realizan al mock server, de tal forma que podemos tracear tanto las peticiones correctas como las incorrectas.

apiary inspector
apiary inspector

Ventajas

Apiary nos aporta una interfaz muy sencilla para editar nuestra API mediante API Blueprint. Otro de los puntos favorables es que mediante el editor generaremos la documentación de nuestro Mock API.

El disponer de un inspector nos ayudará a trazear las diferentes peticiones que se realizan a nuestro mock. En mi caso esto ha sido muy interesante, ya que en un proyecto pude ver peticiones que no tenía contempladas para mockear y testear.

Es un servicio SaaS, por lo que no necesitamos preocuparnos de alojarlo y gestionarlo en nuestros entornos.

Desventajas

La mayor desventaja de Apiary es que es muy simple, es decir, no nos permite meter lógica más allá de lo que es devolver siempre la misma información.

Por ejemplo: Si realizo la petición GET /questions, siempre vamos a recibir el mismo body.

Y si por ejemplo nos interesa disponer de cierta variación de datos como un body aleatorio o generado de datos de la base de datos, no vamos a tener la robustez que buscamos. No obstante, es una muy buena herramienta si no queremos mockear a ese nivel.

SOAPUI

URL: https://www.soapui.org/

Curiosamente yo había usado SoapUI para testear servicios web SOAP, pero no sabía que SoapUI permitiera mockear una API y menos para API REST.  Lo permite y encima en la versión OpenSource.

Para hacer la prueba vamos a mockear el API Mock que hemos publicado en Apiary, así nos ahorraremos el crear nosotros un API. Obtenemos las preguntas mediante una llamada GET al path /questions

soap ui request

Cuando generamos una nueva petición, nos permite crear el REST Mock Service. Lo que me gustaría detallar es que en el caso de SoapUI sí se nos permite generar respuestas dinámicas.

Veamos un ejemplo, donde la primera key de votes tiene el valor ${votes1}

Las respuestas dinámicas las podemos crear mediante el desarrollo de scripts a nivel de petición o de respuesta, ya que SoapUI nos permite definir más de una respuesta por petición.

Vamos a hacer un ejemplo de un script que va a obtener un dato de la base de datos para setearlo en la variable ${votes1} de la respuesta definida.

soapui_script

Si hacemos la petición al servidor, vamos a ver como nos va a setear el resultado de la base de datos.

El lenguaje de scripting es “Groovy script” por lo que si has desarrollado en Groovy o en Java no te va a ser difícil.

Ventajas

SoapUI nos va a permitir desarrollar lógica en nuestros mocks por lo que si necesitamos mayor dinamismo en las respuestas nos va a ser de gran ayuda.

Desventajas

Como desventaja frente a Apiary, es necesario instalar SoapUI en el servidor para poder ejecutar el servidor mock (se puede hacer desde línea de comandos). Y no disponemos de un “inspector”, lo que si es posible es guardar la información del log de SoapUI.

Conclusiones

Si lo que necesitamos es mockear una API sin necesidad de disponer diferentes respuestas, Apiary nos puede ser de gran ayuda. Sin embargo, en el caso de querer “ir más allá” necesitaremos añadir cierta lógica: SoapUI es una opción ideal.

No obstante, recordemos que nuestra intención es mockear, no crear otro servicio web.

 

¿Un QA puede ser también developer?

Assert QA == Developer


¿Es un QA un developer?
Me gusta hablar con la gente. Me gusta compartir opiniones. Me gusta debatir. Tras varias conversaciones con compañeros de trabajo, de los que he podido obtener de una forma u otra feedback de lo que creen que es el trabajo de un QA Engineer, me he propuesto a escribir un post que ayude a romper ciertas barreras mentales en el trabajo que creemos que hace un ingeniero de calidad y ver si en realidad es un developer.

Ingeniería de la calidad del software

La realidad es que poca gente conoce lo que es la ingeniería de la calidad. No es algo conocido, no es algo que llame la atención cuando decides estudiar informática, es la realidad.

Cuando un desarrollador habla de un QA, no piensa que desarrolle, no lo trata como un desarrollador. Es más, muchos piensan que los QAs son desarrolladores frustrados, y no es cierto.

¿Porque cuesta tanto ver la ingeniería QA como una rama de la ingeniería del software, del desarrollo?

Cuando se piensa únicamente desarrollamos pruebas se está muy equivocado. La calidad del software no solo se garantiza con teses.

Desarrollamos teses, ¡por supuesto que sí! Y en muchas ocasiones la lógica que implementamos en el test puede ser más compleja que el código o la funcionabilidad que probamos. Y si… sabemos lo que son los patrones… es más, los aplicamos.

Para garantizar que el software cumple con los requisitos de calidad exigidos los QAs deben diseñar y aplicar las estrategias de calidad necesarias. Y muy seguramente en esas estrategias se implementen diferentes proyectos, desde automatización de pruebas hasta proyectos más complejos, y adivinad, muchos de ellos habrá que desarrollarlos.

Automatización de pruebas

Automatizar pruebas funcionales no siempre significa automatizar el comportamiento de una funcionalidad mediante UI testing. En muchas ocasiones se requiere hacer pruebas funcionales a nivel de API, o probar funcionalidades cross-aplicación y cross-dispotivo.

Aquí os dejo los enlaces a la presentación y explicación de cómo Uber realiza pruebas cross-aplicación y cross-dispotivo para probar sus aplicaciones mediante Octopus.

POST:
https://eng.uber.com/rescued-by-octopus/
SLIDE:
https://docs.google.com/presentation/d/1vYXhkvgLKun72Ix91LQDDWZQdcY5VOBqKVvI1Y6riYo/pub?slide=id.gd8d657045_0_0

VIDEO:
https://www.youtube.com/watch?v=p6gsssppeT0

Para mi es importante entender que un QA no es una persona dedicada únicamente a automatizar pruebas o a realizar testing. Es un ingeniero capaz de desarrollar las herramientas necesarias que ayuden a garantizar la calidad de la aplicación.

Lo que significa que un QA es un desarrollador especializado en la calidad.

Sí, somos un equipo

Una de las cosas que me dijeron era que si el que desarrollaba la tarea hacia bien su trabajo no tenía por qué meter bugs, y ya que sabía lo que estaba desarrollando y donde afectaba él podría hacer las pruebas, por lo tanto, los QAs sobraban.

Bueno, en un mundo perfecto donde el desarrollador está encantado de probar todo lo que hace, y donde el tiempo de entrega no existe, en el que nunca se equivoca, puede ser…¡pues tampoco! Que te diga coca cola o Samsung que no hacen control de calidad de sus productos… igual sus móviles explotarían.

Es necesario desarrollar estrategias y proyectos que garanticen la calidad de nuestras aplicaciones, equipar al equipo de desarrollo con las herramientas suficientes para hacer código de calidad, para agilizar su desarrollo.

Cada equipo somos piezas del desarrollo. Desarrollo, DevOps, Producto, QA etc.

Conclusiones

Antes de ser QA fuí desarrollador, y hoy en día sigo desarrollando casi al mismo nivel que antes, salvo que antes funcionalidades de una aplicación, y ahora herramientas que garantizan su calidad. ¿Qué diferencia hay?

Los QAs también somos desarrolladores, es la realidad, y primero nos lo tenemos que creer nosotros para que los demás se lo crean.