Ejecutando Tests de Postman con CI/CD

Buscando un framework para realizar pruebas de integración, descubrí que nuestros amigos de Postman implementaron una forma muy interesante de probar tu API. En toda colección de Postman, hay una sección llamada Tests donde podés declarar todas las validaciones que quieras hacer sobre la respuesta.

Postman

En el ejemplo oficial anterior, están probando un escenario en el que obtienen todos los items, luego crean un nuevo y finalmente verifican si el elemento fue creado como se esperaba. Eso es genial, pero ¿cómo podemos ejecutar estas pruebas desde la consola, para que podamos agregarlo como un paso en nuestro pipeline de CI/CD? Aquí es donde entra en juego otra gran herramienta del equipo de Postman: Newman.

newman run examples/sample-collection.json

Eso es todo, de esta manera podemos ejecutar cualquier colección de Postman (previamente exportada como un archivo JSON). Parece que encontramos una buena solución para nuestro requisito, pero el mundo real no es tan simple. Probablemente, la API que deseas probar dependa de una base de datos, o peor aún, de APIs externas. Necesitamos descubrir cómo podemos manejar esos escenarios.

¡Docker al rescate!

Gracias a Docker y Docker Compose, podemos ejecutar nuestra API y sus dependencias todas juntas. No estoy escribiendo este post para explicar cómo funciona Docker Compose (hay muchos artículos excelentes sobre eso), así que para simplificar el ejemplo, voy a asumir que nuestra API no tiene dependencias. Solo necesitaríamos los servicios de API y Newman.

version: '3'
services:
  cicd-postman-tests-api:
    container_name: cicd-postman-tests-api
    image: vad1mo/hello-world-rest
  cicd-postman-tests-newman:
    container_name: cicd-postman-tests-newman
    image: postman/newman
    entrypoint: sh
    command: /src/newman-wrapper.sh
    volumes:
      - ./src:/src
    depends_on:
      - cicd-postman-tests-api

Explico la configuración anterior:

  • cicd-postman-tests-api es el servicio de la API en sí. Para reducir la cantidad de código adicional que no es relevante para este tutorial, usé la imagen hello-world-rest, que contiene una API muy simple para interactuar.
  • cicd-postman-tests-newman es el servicio que ejecuta las pruebas, y utiliza la imagen oficial de Newman del equipo de Postman. Como tenemos que esperar hasta que la API esté lista para recibir solicitudes, utilizamos un script como wrapper para esperar. Tené en cuenta que no es suficiente usar la opción depends-on de Docker Compose, ya que esta característica ignora el tiempo de inicio de tu servicio.

Acá está el script wrapper:

#!/bin/sh

set -eu pipefail

echo "Attempting to connect to cicd-postman-tests-api"
until $(nc -zv cicd-postman-tests-api 5050); do
    printf '.'
    sleep 5
done
echo "Connected to cicd-postman-tests-api!"

newman run /src/postman-collection.json

exit 0

Intentamos conectarnos al servicio que se ejecuta en el puerto 5050, y esperamos 5 segundos antes de intentarlo de nuevo. Finalmente, cuando la conexión es exitosa, ejecutamos las pruebas de nuestra colección utilizando el binario de Newman.

¡Listo! Ahora podemos ejecutar las pruebas tan fácil como:

docker-compose up --exit-code-from cicd-postman-tests-newman

Usamos la opción --exit-code-from para especificar que después de que el contenedor cicd-postman-tests-newman se detenga, todos los demás contenedores también deben detenerse, y el código de salida del comando será el que devuelva el contenedor de Newman. De esta manera, forzamos a que el pipeline de CI/CD falle si alguno de los tests falla.

Console

Ahora ¿qué pasa si mi API tiene dependencias? Bueno, simplemente podés agregar esas dependencias (bases de datos, servidores mock, etc.) como services en el docker-compose.yml, esa es la razón principal por la que introducimos Docker en el stack. Podés ajustar el script newman-wrapper.sh para que también espere por esos servicios.

Conclusión

Gracias a Postman, Newman y Docker, podemos construir y mantener pruebas de integración que pueden ser ejecutadas tanto en pipelines de CI/CD como en entornos locales, permitiendo a los desarrolladores probar el servicio completo en su propio espacio controlado, de manera más rápida y sencilla.