Cómo automatizar las pruebas de un extremo a otro de la API REST en un entorno de CI con Postman y Newman

Postman es una gran herramienta para explorar las API REST. Puede crear solicitudes y probarlas para obtener comentarios rápidos. Luego, puede conservarlos como colecciones para asegurarse de que el conocimiento no se pierda.

Newman, la versión CLI de Postman, le permite llevarlo al siguiente nivel y transformar una colección en un conjunto de pruebas automatizadas de un extremo a otro. Esta suite se ejecutará en la herramienta de CI que elija. En este artículo exploraré los beneficios de hacerlo y le mostraré cómo configurarlo.

intro

¿Qué es una prueba de extremo a extremo en el contexto de una API?

Probar la nomenclatura es complicado. Teniendo en cuenta la pirámide de pruebas, podemos imaginarlas como pruebas de muy alto nivel. Estas pruebas confirman que una API REST en particular funciona según lo previsto, y trata los componentes internos como una caja negra. No involucramos ninguna interfaz de usuario en el proceso, lo que ayuda a reducir la descamación.

poke-e2e

por geek & poke / CC BY

Las pruebas inestables son extremadamente molestas, como lo han experimentado todos los desarrolladores en algún momento. En lugar de golpearnos la cabeza contra la pared tratando de arreglar lo que no se puede arreglar, podemos mitigar el problema usando pruebas de nivel más bajo.

¿Por qué debería hacerme estas pruebas?

Hay dos escenarios diferentes que me gustaría cubrir:

El primero es probar sus propias API REST. Estas pruebas añaden una capa adicional de confianza. Seguramente, estás utilizando una combinación saludable de diferentes pruebas (unidad, integración, funcional,…). Las pruebas de un extremo a otro pueden ser la confirmación final de que todo se ve bien.

El segundo caso es probar API que no controlas. En mis últimos proyectos, la mayoría de los datos que consumimos provenían de API proporcionadas por otros equipos. Más de una vez pasé medio día depurando un error en mi aplicación, solo para notar que una API descendente estaba bloqueada todo el tiempo. Las pruebas automatizadas cubren esa integración y ayudan a aislar los problemas.

Documentación viva

Una colección de pruebas que se ejecutan con regularidad sirve como la mejor documentación para una API. ¿Has buscado algo en alguna wiki corporativa últimamente? Si encuentras algo, deberías estar feliz. Probablemente estará incompleto. O simplemente mal. Tiempos divertidos.

Supervisión

En ambos casos, estas pruebas pueden transformarse de una puerta de enlace en el proceso de construcción a una herramienta de monitoreo activa. Al ejecutarlos constantemente, se asegura de que la API siga comportándose como espera. De lo contrario, se activarán las alarmas correctas. No querrá darse cuenta de que algo anda mal solo cuando un cliente se queja.

¿Por qué no utilizar pruebas de contrato impulsadas por el consumidor en su lugar?

Gran pregunta, si se me permite decirlo. Los CDC son una excelente manera de garantizar que una API se ajuste a lo que un cliente espera de ella. Si puede configurarlos correctamente, reemplazarán las pruebas de un extremo a otro casi por completo. Recuerde, siga llevando las pruebas a un nivel más bajo siempre que pueda.

Sin embargo, no funcionan en todas las situaciones. Si no controla tanto al proveedor como al consumidor, debe depender de otra parte. Si no cumplen con su parte del contrato, las pruebas serán inútiles. Algunos equipos simplemente no están en la posición de realizar pruebas continuamente contra un contrato. Ejecutar sus propias pruebas podría ser su mejor opción.

De todos modos, habiendo expuesto el fundamento, es hora de un código .

Crear una colección de cartero

La colección

Estamos definiendo una serie de llamadas que se ejecutarán secuencialmente dentro de nuestro CI. Cada llamada ejecuta una solicitud contra la API. Luego ejecuta algunas pruebas para verificar que la solicitud fue exitosa, verificando el código de estado y el cuerpo también.

Para crear la colección, suelo usar la aplicación Postman. Me gusta extraer cosas como URL y parámetros a un entorno. Luego, configurarlo se vuelve más fácil y no tiene ninguna información confidencial en la colección en sí. Su historial es un lugar conveniente para comenzar a construir esta colección.

Una vez que esté satisfecho con la colección, puede exportarla como un archivo JSON. Ese archivo se puede confirmar en el control de código fuente para que sirva como base para la canalización que ejecutará las pruebas. Hay una versión Pro y Enterprise que ayuda a administrar las colecciones, que realmente no he probado. Aún así, un buen gitrepositorio es más que suficiente para empezar a funcionar.

cartero exportador

Ejecutando la colección

Hasta ahora hemos estado usando Postman normal y nada más. Ahora es el momento de que Newman brille. ¿De qué estoy hablando, de todos modos? Citaré los documentos oficiales directamente:

Newman es un corredor de colección de línea de comandos para Postman. Le permite ejecutar y probar una colección de cartero directamente desde la línea de comandos.

¡Qué bueno que aclaramos eso! Se instala como un paquete npm, lo que puede resultar en un package.jsonsimple como este:

{ "name": "postman-utils", "version": "0.0.1", "private": true, "description": "Postman utilities", "scripts": { "newman": "node_modules/.bin/newman run" }, "dependencies": { "newman": "^4.4.1" } } 

as mentioned before, you don't want to hardcode variables like URLs, parameters or, God forbid, passwords in that collection. It's not flexible, and it's not safe. Instead, I like to use a configuration file which includes all these values. But if we want to commit that file, we still need to figure out a way to avoid putting secrets in there. I use it as a template and replace values at runtime with envsubst. The configuration file looks like this

{ "id": "425cf4df-d994-4d91-9efb-41eba1ead456", "name": "echo", "values": [ { "key": "host", "value": "${HOST}", "enabled": true } ] } 

You can orchestrate this with a simple bash script. The script injects the variables into the template, runs newman, and deletes the files to avoid leaks. It goes very well with gopass, where you can safely store your secrets and fetch them through the script.

setup-newman() { settings=/tmp/settings.json.$$ result=/tmp/variables.json.$$ # shellcheck disable=SC2064 trap "rm -f \"$settings\" \"$result\"" EXIT } run-newman() { local service=${1?You need to provide the service to check} envsubst  "$settings" npx newman run "$service.json" \ -e "${settings}" \ --export-environment "${result}" } 

that helper can be called with the collection that you want to test. Exported variables will be picked by envsubst. npx gives us a little bit more of flexibility finding the newman binary, in case you don't want to use a package.json but have it globally installed.

goal_check-service() { setup export SERVICE_PASSWORD=${SERVICE_PASSWORD:-$(gopass store/service/password)} run_newman service } 

Tests

Doing a request is but the first step. Remember, we aim to build a test suite. We have a convenient test tab in Postman that we can use to write our tests.

pestaña de prueba

Our tests are written in JavaScript, using Chai. Let's say I want to test that my call delivered a list of results, I could do it like this:

var getResults = function() { var jsonData = pm.response.json(); return jsonData['results']; }; pm.test("Request was successful", function () { pm.response.to.have.status(200); }); pm.test("There are results", function () { pm.expect(getResults().length).to.be.above(0); }); 

More details can be found here

Building flows

All the calls in a collection get executed sequentially. This offers us the opportunity to test whole flows instead of just single calls. One such a flow for a /posts resource is:

  • Get a list of all posts
  • Fetch the first post in the list
  • Update the post

We'll build a suite of parametrized tests that will continue to work over time, not just the first time that you ran it. An important part of this is modifying the environment in a request. That is our way of transmitting parameters between requests. Let's say our first request was successful, as corroborated by our tests. Then we store the id on a variable that will be used to fetch a particular entity.

// First result in the list var post = getResults()[0]; // Pass variables to other stages pm.environment.set("id", post.id) 

The next request can use that parameter as any that we set manually.

Ignoring calls based on a condition

Flows might need also need some logic to skip certain requests. Let's say you have a request that is creating a new entity through a POST. You want to have that request, but you may not want to run it on every commit. Maybe you just want do it once per day. In that case, we'll skip the test based on a certain variable.

// Do not run create request in sequence, unless executeCreate is set to true if(!pm.environment.get("executeCreate")) { postman.setNextRequest('Get other posts') } 

The variable goes into the configuration file, and is set to a environment variable that gets injected through our script, as I showed above.

Time for some continuous integration

At this point you should have a collection that runs locally. Running this once is fine, but why not run it for every commit? Or maybe every hour, if you want to check an API that you don't control?

Your CI pipeline is a perfect place to do this. I'm going to use CircleCI for my example, but any CI will do. I run the tests inside a docker image that I built which includes all the required dependencies. There is an official Docker image provided by Postman already. However, it does not contain envsubst and it uses an older NodeJS version.

The helper script that we built in the step before will work without any changes inside CircleCI. We just have to provide the required secrets as variables. This is the job:

 healthcheck: docker: - image: sirech/newman-executor:12.6 steps: - checkout - run: ./go test-e2e 

which will produce a report similar to this:

salida

What about the alternatives?

Many frameworks provide their own way of running tests against a running API. In Spring Boot, for instance, you can use MockMvc to test controllers. You can use both, in my view. First the native tests, so to speak, and then layer Postman Tests on top.

And let's not forget about good ol' curl. I had a huge collection of curl commands with which I tested an API that was needed for my last project. However, managing that becomes more and more tedious over time. If you want to use send complex requests, like certificates or cookies, Postman is way more convenient to use. Moreover, you can use JavaScript instead of bash, which can make things a bit easier to read and maintain.

What else?

Esto ya es bastante y es solo el comienzo. Todo lo que hagas con una API también puedes automatizarlo. Por ejemplo, en mi proyecto anterior teníamos una colección que ejecutaba un flujo OAuth. Eso nos dio un token que podríamos usar para realizar solicitudes contra un punto final autorizado.

Un repositorio para usar como ejemplo

Aquí hay un repositorio para una aplicación de Kotlin que ejecuta una colección de Postman como una prueba e2e. Puede servir como un kit de inicio para comenzar con pruebas API de extremo a extremo de alta calidad.