Ir al contenido

Pruebas de API con Postman

Postman es un cliente HTTP: arma una petición real, la envía por la red, y solo puede ver dos cosas, lo que salió (verbo, URL, headers, body) y lo que volvió (status, headers, body). No lee el código fuente ni sabe en qué framework o lenguaje está escrito el backend: prueba el contrato HTTP desde afuera, exactamente como lo haría el front en Angular o cualquier otro cliente real.

Esto es lo que se llama una prueba de caja negra: verifica que la caja responda como se espera, sin abrirla para revisar por dentro. El otro extremo son las pruebas con JUnit: prueban un método Java (la unidad es el método) y se escriben en Java.

En este curso se usan Collections, Environments y Tests, pero Postman ofrece bastante más. Vale la pena saber que existe, para cuando se necesite en un proyecto real:

  • Pre-request scripts: código que corre justo antes de enviar la petición, por ejemplo para calcular una fecha o armar un token.
  • Collection Runner: corre todas las peticiones de una Collection en secuencia, incluso repitiendo el ciclo con un archivo de datos (CSV o JSON).
  • Newman: el mismo Collection Runner, pero desde la terminal en vez de la aplicación de Postman. Sirve para automatizar la ejecución de las pruebas, por ejemplo como parte de un pipeline de Integración Continua. Se instala en el taller de ambiente de desarrollo.
  • Mock Servers: simulan las respuestas de una API que todavía no existe, para que el front pueda avanzar en paralelo.
  • Monitors: ejecutan una Collection automáticamente según un horario, para detectar si un servicio se cae.
  • Documentación y Workspaces: generan documentación navegable de la API a partir de la Collection, y permiten compartirla con el equipo.

Antes de escribir un pm.test(), dos preguntas:

  1. ¿Qué datos necesita la prueba? Si se va a consultar un artista por su id, ese artista tiene que existir de antemano: o ya está en los datos de prueba, o se crea con un POST justo antes (se ve en “Tests encadenados”, más abajo).
  2. ¿Qué se va a verificar, y en qué escenarios? Es la misma idea de JUnit: un escenario positivo verifica el caso feliz (el código de estado esperado, la forma del body); un escenario negativo verifica qué pasa cuando algo sale mal o se sale de lo común (un id que no existe, una lista vacía, un dato duplicado). La estrategia mínima para cada endpoint es al menos un escenario positivo y uno negativo.

Después de recibir una respuesta HTTP, Postman ejecuta JavaScript para verificar formalmente lo esperado. Sin tests hay que revisar visualmente status, body y headers; con tests la verificación es repetible y automática en toda la Collection.

  1. Enviar la petición HTTP.
  2. Recibir status, headers, body y tiempo de respuesta.
  3. Ejecutar el JavaScript de la pestaña Scripts (opción Post-response).
  4. Mostrar PASS o FAIL para cada pm.test() en Test Results.
pm.test("El artista tiene id y nombre", function () {
const artista = pm.response.json();
pm.expect(artista).to.have.property("id");
pm.expect(artista).to.have.property("nombre");
});
  • pm es el objeto global para respuesta, entorno y tests.
  • pm.test(nombre, función) define el caso visible en Test Results.
  • el nombre expresa qué se verifica.
  • la función contiene una o más aserciones.
  • pm.expect(valor real) inicia una aserción Chai y la cadena posterior expresa el valor esperado.
pm.expect(pm.response.code).to.equal(200);
pm.expect(body).to.be.an("array");
pm.expect(body.length).to.be.above(0);
pm.expect(artista).to.have.property("albumes");
pm.expect(artista.nombre).to.be.a("string");
pm.expect(artista.albumes).to.be.empty;
pm.expect(pm.response.responseTime).to.be.below(500);
pm.expect(pm.response.headers.get("Content-Type"))
.to.include("application/json");

El status dice si la petición funcionó, no si lo hizo bien. Una respuesta 200 con el cuerpo equivocado, o un 204 que nunca borró nada, pasa un test que solo mira el código. Conviene verificar cinco capas, de la más superficial a la más profunda:

Capa Qué se verifica Ejemplo
Status El código esperado 201 al crear
Cabeceras El formato de la respuesta Content-Type incluye application/json
Forma del cuerpo Qué campos hay y de qué tipo albumes es un arreglo; id es un número
Valores El contenido coincide con lo enviado o con la URL El nombre es el que se envió; el id es el de la URL
Efectos Un GET posterior confirma que el cambio ocurrió Tras un DELETE, el GET responde 404

En los errores, además del status, se verifica el cuerpo: el back responde un ApiError (status, message, timestamp) y el message debe nombrar la causa, porque es el texto que el front le muestra a la persona.

Cada patrón siguiente aplica el principio de escenarios positivos y negativos a un verbo HTTP distinto.

Verifica 200, que el body sea un arreglo no vacío, que cada artista tenga id y nombre, que albumes exista como arreglo para confirmar ArtistaDTOCompleto, y que Content-Type incluya application/json.

Verifica que el body sea un objeto y no un arreglo, que el id coincida con el de la URL, que nombre sea un string no vacío y que albumes sea un arreglo. Cada álbum trae id y titulo.

Verifica 201 Created, un identificador numérico positivo asignado por el servidor y que nombre coincida con el enviado. La respuesta de un POST es el DTO básico, sin albumes: la colección se ve en el GET por identificador. Este back no envía el header Location, así que no se verifica.

const nuevo = pm.response.json();
pm.test("Status code es 201 Created", () =>
pm.expect(pm.response.code).to.equal(201));
pm.test("id fue asignado por el servidor", () =>
pm.expect(nuevo.id).to.be.above(0));
pm.test("El nombre coincide con el enviado", () =>
pm.expect(nuevo.nombre).to.equal(pm.environment.get("nombreArtista")));
  • PUT verifica 200, que el identificador no cambie, que los campos enviados se actualicen, que un GET posterior confirme el cambio y que los álbumes se conserven.
  • DELETE verifica 204 No Content, sin cuerpo, y que un GET posterior al mismo identificador responda 404.
  • una regla violada, por ejemplo un nombre repetido, verifica 400 y un cuerpo ApiError con status: 400, un message que nombre la regla y un timestamp.
  • un identificador inexistente verifica 404, un cuerpo ApiError sin propiedad nombre y un mensaje que diga que el recurso no existe.

Laboratorio de respuestas

Cambiar la respuesta simulada, ejecutar los tests y observar por qué pasan o fallan.

Código de estado

Respuesta simulada

Status recibido: 200 · 38 ms
Tests que se ejecutarán
pm.expect(pm.response.code).to.equal(200);
Test Results

Selecciona una respuesta y ejecuta.

// POST: guardar el id generado
pm.environment.set("artistaId", nuevo.id);
// GET: usarlo en /artistas/{{artistaId}}
pm.expect(pm.response.json().id)
.to.equal(Number(pm.environment.get("artistaId")));
// DELETE: limpiar la variable
pm.environment.unset("artistaId");

El encadenamiento crea un recurso, consulta el mismo identificador y finalmente lo elimina. Las variables de entorno evitan copiar manualmente el valor entre requests.

1. ¿Cuándo ejecuta Postman los tests de una petición?
2. ¿Qué define pm.test()?
3. ¿Qué status se espera al crear correctamente un artista?
4. ¿Cómo se comprueba que el body es un arreglo?
5. ¿Qué permite encadenar POST, GET y DELETE?
6. ¿Qué debe verificar un GET inexistente?
7. ¿Por qué Postman puede probar un backend sin importar en qué lenguaje esté escrito?
8. ¿Para qué sirve Newman?
9. ¿Cuál es la estrategia mínima de pruebas para un endpoint?
10. Un DELETE responde 204, pero el recurso sigue existiendo. ¿Qué test lo detecta?
11. Una petición viola una regla de negocio y el back responde 400. ¿Por qué conviene verificar también el message del cuerpo?