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.

Testing Restful API response

Introducción

Hace un tiempo publiqué un artículo sobre cómo “testear” funcionalmente la Restful API, los podéis leer en:

https://qajungle.com/restful-api-functional-testing/
http://blog.engineering.ticketbis.com/rest-api-functional-testing/

En el proyecto en el que he estado trabajando he necesitado tener que validar que el json de respuesta tenga una estructura en concreto y que los campos cumplan ciertas condiciones.

Al principio no necesitaba validar grandes cosas por lo que simplemente creé una clase utilidad que me ayudará a comprobar que el json de respuesta tendría los campos que esperábamos, pero esto suponía varios problemas o deficiencias:

  • Únicamente podría validar la existencia del campo, pero no ningún tipo de restricción.
  • Duplicidad de código a medida que se añaden más pruebas.
  • Un cambio en la definición del json implicaría refactorizar las pruebas.

Debido a estos motivos pensé que tenía que haber una forma más sencilla que permitiera suplir estas deficiencias. Me acordé de cómo cuando construimos un XML definimos su esquema de tal forma que validamos su estructura de una forma sencilla, por lo que me puse a investigar y encontré lo siguiente:

http://json-schema.org/

Mediante json-schema definimos la estructura que tiene que cumplir nuestro json por lo que podía ser una buena baza ya que nos permitiría:

  • Validar la estructura y ciertas restricciones.
  • Únicamente tenemos los esquemas por lo que evitamos duplicidad de código.
  • Un cambio en la definición únicamente nos obliga a cambiar el esquema y no refactorizar el código.

Solucionamos los problemas con los que nos hemos visto y encima mantenemos cierta independencia en lo que es la validación de la estructura del json y el test funcional.

Configuración del proyecto

Lo primero que tenemos que hacer implementar un validador json que nos permita comparar nuestro esquema con la respuesta obtenida, para ello he hecho uso del plugin:

https://github.com/fge/json-schema-validator
Lo cierto es que actualmente el plugin no tiene un mantenimiento pero está desarrollado para la versión actual del esquema v4.

En nuestro caso como hemos desarrollado sobre un proyecto grails lo añadiremos en el BuildConfing.groovy como dependencia:

dependencies {
...
compile "com.github.fge:json-schema-validator:2.2.6"
}

Clase utilidad

Para poder simplificar y no duplicar código creamos una clase utilidad:

Básicamente lo que hacemos es cargar el esquema json que hemos definido y validarlo frente a la respuesta obtenida de la llamada a la API.

Una de las características es que nos proporciona un reporte de los errores de validación por lo que nos dará información de los incumplimientos.

Definición del esquema

Siguiendo el ejemplo visto en el artículo https://qajungle.com/restful-api-functional-testing/ vamos a generar el esquema para la respuesta de los post.

Únicamente definimos las tres propiedades que esperamos: id, title, body e indicamos que son campos requeridos por lo que si en la respuesta no obtenemos el campo o es nulo no pasará la validación.

Más información sobre la definición de esquemas:

http://json-schema.org/examples.html

Validando la respuesta

Para validar la respuesta únicamente llamaríamos al método de la clase utilidad pasándole la ruta del esquema correspondiente:

JsonValidationUtils.validateJson(response.text, "src/java/schemes/post.json")

Conclusiones

Si nos encontramos con el caso que tener que validar la estructura de la respuesta obtenida de Restful API puede ser interesante utilizar algún tipo de validador ya que nos aporta mayor potencia para el testeo de la respuesta así como evitar duplicidad de código y evitarnos tener que hacer gran refactorización de nuestro código.

Bibliografía

  1. http://json-schema.org/
  2. https://github.com/fge/json-schema-validator