ISIS2603 · Pruebas de API
Tests en Postman
pm.test · pm.expect · Chai · Aserciones esenciales
Sección 01

¿Qué es un test
en Postman?

La idea central antes de escribir una sola línea de código

Cuando envías una petición HTTP con Postman, obtienes una respuesta. Un test es un fragmento de JavaScript que Postman ejecuta automáticamente después de recibir esa respuesta, y que verifica que cumple con lo que esperabas.

"Un test no adivina si algo funcionó — lo afirma formalmente."
Diferencia entre mirar el resultado manualmente y verificarlo automáticamente

Sin tests tienes que revisar visualmente el código de estado, el body y los headers. Con tests, Postman hace esa verificación por ti, de forma repetible y automática en toda la Collection.

¿Dónde se escriben?

En la pestaña "Tests" (también llamada "Post-response") de cada request en Postman. Son JavaScript puro que se ejecuta después de recibir la respuesta, con acceso al objeto pm.

1

Envías la petición HTTP

Postman ejecuta el request y recibe la respuesta del servidor.

2

Postman ejecuta tu JavaScript

El código en la pestaña Tests corre automáticamente, con acceso a la respuesta vía el objeto pm.

3

Cada pm.test() produce PASS o FAIL

Los resultados aparecen en "Test Results". Si algo falla, Postman te dice exactamente qué aserción no se cumplió.

Pestaña Tests · JavaScript
// Se ejecuta automáticamente DESPUÉS de recibir la respuesta pm.test("El servidor respondió correctamente", function() { pm.expect(pm.response.code).to.equal(200); }); // Resultado en Test Results: // ✓ El servidor respondió correctamente
Sección 02

Anatomía de pm.test()

Diseccionando cada parte. Haz clic en cualquier elemento del código.

pm.test("El artista tiene id y nombre", function() { pm.expect(pm.response.code) .to.equal(200); });
pm
pm.test()
Nombre
Callback
pm.expect()
pm.response
Aserción Chai
Valor esperado
Selecciona una parte del código
Haz clic en cualquier elemento para ver su explicación
Cada parte tiene un rol preciso en la estructura del test.

Esta estructura es siempre la misma. Una vez que la entiendes, puedes escribir cualquier test combinando diferentes propiedades de pm.response con diferentes métodos de aserción Chai.

Sección 03

pm.expect()
y la biblioteca Chai

Cómo expresar lo que esperas en lenguaje casi natural

Postman usa la biblioteca Chai para las aserciones. Chai permite escribir verificaciones que se leen como inglés. Toda aserción sigue este patrón:

Patrón general
pm.expect( valor_real ) . to.equal / to.be.a / to.have.property ... ( valor_esperado ) ↑ ↑ ↑ lo que método Chai lo que obtuviste (la aserción) esperas

Los métodos de Chai se encadenan con puntos. Palabras como to, be, a, an son solo conectores de legibilidad — no cambian el resultado:

Cadenas equivalentes
pm.expect(body).to.be.an('array'); // más legible — "expect body to be an array" pm.expect(body).be.an('array'); // sin "to" — idéntico pm.expect(body).an('array'); // mínimo — idéntico
.to.have.property('nombre') se lee: "espero que [objeto] tenga la propiedad 'nombre'"
Chai está diseñado para que el código sea autodocumentado

La negación se agrega poniendo .not antes del método:

Negación con .not
// Afirmativo: body ES un objeto pm.expect(body).to.be.an('object'); // Negado: body NO ES un arreglo pm.expect(body).not.to.be.an('array'); // ⚠️ En JS: typeof [] === 'object' // Para verificar un objeto puro (no arreglo) se necesitan ambas líneas.
Sección 04

Las aserciones esenciales

Las 8 que más usarás en el curso. Haz clic en una tarjeta.

🎯.to.equal(v)
Igualdad estricta (===). Para código HTTP, id exacto.
🏷️.to.be.a('tipo')
Verifica tipo JS: 'string', 'number', 'boolean'…
📦.to.be.an('array')
Es un arreglo. Diferente de 'object' en JS.
🔍.to.have.property(k)
El objeto tiene la clave. Para verificar el contrato DTO.
📏.to.be.above(n)
Mayor que n. Para length>0, id>0.
🔎.to.include(s)
String contiene subcadena. Para Content-Type, Location.
🈳.to.be.empty
Longitud 0. Para albumes:[] recién creados.
⏱️.to.be.below(n)
Menor que n. Para tiempo de respuesta.
🎯 .to.equal(v) — Igualdad estricta

Verifica que el valor real es exactamente igual al esperado, en valor y tipo. Usa === internamente — 200 ≠ "200".

Ejemplos
// Código HTTP exacto pm.test("Status es 200", function() { pm.expect(pm.response.code).to.equal(200); }); // Id coincide con la URL /api/artistas/1 const artista = pm.response.json(); pm.test("id es 1", () => pm.expect(artista.id).to.equal(1));
🏷️ .to.be.a('tipo') — Tipo JS

Detecta errores de serialización: si activo llega como "true" (string) en vez de true (boolean), esta aserción lo atrapa.

Ejemplos
const a = pm.response.json(); pm.test("nombre es string", () => pm.expect(a.nombre).to.be.a('string')); pm.test("id es number", () => pm.expect(a.id).to.be.a('number')); pm.test("activo es boolean", () => pm.expect(a.activo).to.be.a('boolean'));
📦 .to.be.an('array') — Arreglo

En JS, typeof [] devuelve 'object'. Por eso Chai tiene .be.an('array') que usa Array.isArray() internamente — el único modo correcto.

Ejemplos
const body = pm.response.json(); // GET /artistas → colección pm.expect(body).to.be.an('array'); // GET /artistas/1 → objeto puro (no arreglo) pm.expect(body).to.be.an('object'); pm.expect(body).not.to.be.an('array'); // necesario en JS // Campo albumes en DetailDTO pm.expect(body.albumes).to.be.an('array');
🔍 .to.have.property(k) — Verificar campo

Si el campo existe, el backend está devolviendo el DTO correcto. Es la forma de verificar el contrato DetailDTO.

Ejemplos
const a = pm.response.json(); // Verifica contrato ArtistaDetailDTO pm.test("Tiene campos DetailDTO", function() { pm.expect(a).to.have.property('id'); pm.expect(a).to.have.property('nombre'); pm.expect(a).to.have.property('albumes'); // DetailDTO! }); // Con forEach en colecciones: body.forEach(x => pm.expect(x).to.have.property('albumes'));
📏 .to.be.above(n) — Mayor que n

Para verificar que un arreglo no está vacío (length > 0) o que un id fue asignado (id > 0).

Ejemplos
pm.expect(body.length).to.be.above(0); // arreglo no vacío pm.expect(nuevo.id).to.be.above(0); // id asignado por BD pm.expect(nuevo.nombre.length).to.be.above(0); // nombre no vacío
🔎 .to.include(s) — Contiene subcadena

Más flexible que .equal(). El Content-Type puede venir como "application/json" o "application/json;charset=UTF-8".include() maneja ambos.

Ejemplos
pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json'); pm.expect(pm.response.headers.get('Location')).to.include('/api/artistas/'); pm.expect(err.message).to.include('999'); // mensaje menciona el id buscado
🈳 .to.be.empty — Colección vacía

Verifica longitud 0. Un artista recién creado tiene albumes:[] — debe existir como arreglo y estar vacío.

Ejemplo
const nuevo = pm.response.json(); // POST → recién creado pm.test("albumes inicia vacío", function() { pm.expect(nuevo.albumes).to.be.an('array'); // debe existir pm.expect(nuevo.albumes).to.be.empty; // sin paréntesis });
⏱️ .to.be.below(n) — Menor que n

Para tiempo de respuesta. Una API bien implementada no debería tardar más de 500ms en lecturas simples.

Ejemplo
pm.test("Respuesta rápida", () => pm.expect(pm.response.responseTime).to.be.below(500));
Sección 05

Laboratorio interactivo

Cambia el escenario y observa cómo cambia el resultado de los tests

Lab 1 — Código de estado HTTP

GET /api/artistas/1
Tests (pestaña Tests)
pm.test("Status es 200", function() { pm.expect(pm.response.code) .to.equal(200); }); pm.test("Tiempo < 500ms", function() { pm.expect(pm.response.responseTime) .to.be.below(500); });
Test Results
Presiona ▶ Ejecutar

💡 El test "Status es 200" solo pasa cuando el escenario es 200. Prueba los otros escenarios para ver los FAIL.

Lab 2 — Estructura del body (DetailDTO)

GET /api/artistas
Tests
const body = pm.response.json(); pm.test("Body es arreglo", () => pm.expect(body).to.be.an('array')); pm.test("Arreglo no vacío", () => pm.expect(body.length).to.be.above(0)); pm.test("Es DetailDTO", function() { body.forEach(a => pm.expect(a).to.have.property('albumes') ); });
Test Results
Selecciona escenario y ejecuta

💡 Prueba "Sin albumes": el test detecta que el backend devolvió DTO básico en vez de DetailDTO.

Lab 3 — POST: 201 vs 200 (error común)

POST /api/artistas
Tests
const n = pm.response.json(); pm.test("Status 201 Created", () => pm.expect(pm.response.code).to.equal(201)); pm.test("Location presente", () => pm.expect( pm.response.headers.get('Location') ).to.include('/api/artistas/')); pm.test("albumes vacío", function() { pm.expect(n.albumes).to.be.an('array'); pm.expect(n.albumes).to.be.empty; });
Test Results
Selecciona escenario y ejecuta

💡 Devolver 200 en un POST es el error de implementación más frecuente. Los tests lo detectan inmediatamente.

Sección 06

Patrones por operación

Qué verificar en cada tipo de request HTTP

  • Código HTTP es 200 — nunca 201 ni 204
  • Body es array para colecciones, object para recurso individual
  • Arreglo no está vacío si hay datos de prueba
  • Cada elemento tiene campos del DetailDTO — incluyendo albumes
  • El id coincide con el id de la URL (GET individual)
  • Content-Type incluye 'application/json'
GET /api/artistas — Tests completos
const body = pm.response.json(); pm.test("Status 200", () => pm.expect(pm.response.code).to.equal(200)); pm.test("Body es arreglo", () => pm.expect(body).to.be.an('array')); pm.test("No vacío", () => pm.expect(body.length).to.be.above(0)); pm.test("Content-Type JSON", () => pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json')); pm.test("Es ArtistaDetailDTO", function() { body.forEach(a => { pm.expect(a).to.have.property('id'); pm.expect(a).to.have.property('nombre'); pm.expect(a).to.have.property('albumes'); pm.expect(a.albumes).to.be.an('array'); }); });
  • Código HTTP es 201nunca 200
  • Header Location contiene la URL del recurso creado
  • El id fue asignado por la BD — número mayor a 0
  • Los campos enviados coinciden con los del body de respuesta
  • Las colecciones (albumes) inician vacías
  • Guardar el id con pm.environment.set()
POST /api/artistas — Tests completos
const nuevo = pm.response.json(); pm.test("Status 201 Created", () => pm.expect(pm.response.code).to.equal(201)); pm.test("Header Location", () => pm.expect(pm.response.headers.get('Location')).to.include('/api/artistas/')); pm.test("Id asignado", () => pm.expect(nuevo.id).to.be.above(0)); pm.test("Nombre coincide", () => pm.expect(nuevo.nombre).to.equal("Maluma")); pm.test("albumes inicia vacío", function() { pm.expect(nuevo.albumes).to.be.an('array'); pm.expect(nuevo.albumes).to.be.empty; }); pm.environment.set("artistaId", nuevo.id); // 💾 guardar
  • Código HTTP es 200
  • El id no cambió — PUT nunca cambia el id
  • Los campos actualizados reflejan los valores enviados
  • Las colecciones (albumes) se conservan
PUT /api/artistas/3 — Tests
const act = pm.response.json(); pm.test("Status 200", () => pm.expect(pm.response.code).to.equal(200)); pm.test("Id no cambió", () => pm.expect(act.id).to.equal(3)); pm.test("Genero actualizado", () => pm.expect(act.genero).to.equal("Rock / Latin / Pop")); pm.test("albumes conservados",() => pm.expect(act.albumes).to.be.an('array'));
  • Código HTTP es 200 (devuelve el recurso eliminado)
  • Body contiene el DetailDTO del recurso eliminado
  • El id del body coincide con el id de la URL
  • Limpiar con pm.environment.unset()
DELETE /api/artistas/2 — Tests
const del = pm.response.json(); pm.test("Status 200", () => pm.expect(pm.response.code).to.equal(200)); pm.test("Body es DetailDTO", function() { pm.expect(del).to.have.property('id'); pm.expect(del).to.have.property('albumes'); }); pm.test("Id eliminado es 2", () => pm.expect(del.id).to.equal(2)); pm.environment.unset("artistaId"); // 🧹 limpiar
  • 404 para recurso no encontrado — nunca 200 con null
  • 409 para recurso duplicado — nunca 400
  • Body del error tiene status, error y message
  • El message cita el id o campo que causó el error
  • Body NO tiene campos de un recurso válido (sin nombre, sin albumes)
GET /api/artistas/999 → 404 — Tests
const err = pm.response.json(); pm.test("Status 404", () => pm.expect(pm.response.code).to.equal(404)); pm.test("Body es error, no artista", function() { pm.expect(err).to.have.property('error'); pm.expect(err).to.have.property('message'); pm.expect(err).not.to.have.property('nombre'); // no es artista }); pm.test("message menciona el id", () => pm.expect(err.message).to.include('999'));
Sección 07

Tests encadenados

Cómo pasar el id entre requests usando variables de entorno

En una Collection los requests se ejecutan en orden. Puedes guardar el id de un recurso creado con pm.environment.set() y usarlo en requests siguientes con la sintaxis {{artistaId}}.

Patrón CRUD: Crear → Consultar → Actualizar → Eliminar
El id fluye de un request al siguiente vía variables de entorno
1

POST — Crear y guardar el id

Después del 201, guarda el id generado en una variable de entorno.

Request 1: POST
const nuevo = pm.response.json(); pm.test("Status 201", () => pm.expect(pm.response.code).to.equal(201)); pm.environment.set("artistaId", nuevo.id); // Ahora {{artistaId}} está disponible en toda la Collection
2

GET — Consultar con {{artistaId}}

Usa la variable en la URL: GET /api/artistas/{{artistaId}}

Request 2: GET /api/artistas/{{artistaId}}
const a = pm.response.json(); pm.test("Status 200", () => pm.expect(pm.response.code).to.equal(200)); pm.test("Id coincide", () => pm.expect(a.id).to.equal(pm.environment.get("artistaId")));
3

DELETE — Eliminar y limpiar

Al final, limpia la variable para poder repetir la Collection.

Request 3: DELETE /api/artistas/{{artistaId}}
pm.test("Status 200", () => pm.expect(pm.response.code).to.equal(200)); pm.environment.unset("artistaId"); // 🧹 Collection queda limpia
Sección 08

Guía de referencia

Todo en una página para consultar mientras programas

pm.response — Propiedades

PropiedadTipoDescripción
pm.response.codenumberCódigo HTTP numérico (200, 201, 404…)
pm.response.statusstringTexto del estado ("OK", "Created"…)
pm.response.responseTimenumberTiempo en milisegundos
pm.response.json()object/arrayBody parseado como JSON
pm.response.text()stringBody como texto plano
pm.response.headers.get(k)stringLeer un header HTTP por nombre

Aserciones Chai — Referencia

AserciónCuándo usarla
.to.equal(v)Código HTTP, id exacto, nombre exacto
.to.be.a('string')Verificar tipo de un campo
.to.be.an('array')GET colecciones, campo albumes
.to.have.property(k)Verificar contrato DetailDTO
.to.be.above(n)length > 0, id > 0
.to.be.below(n)Tiempo de respuesta
.to.include(s)Content-Type, Location header
.to.be.emptyColecciones en recursos recién creados
.not.to.XNegar cualquier aserción

Códigos HTTP — Reglas del curso

OperaciónCódigo esperadoError común
GET exitoso200 OKDevolver 404 cuando existe
POST exitoso201 CreatedDevolver 200 — muy frecuente
PUT exitoso200 OKDevolver 201 al actualizar
DELETE exitoso200 OKNo devolver el recurso eliminado
No encontrado404 Not FoundDevolver 200 con null
Duplicado409 ConflictDevolver 400 en vez de 409
Datos inválidos400 Bad RequestDejar caer como 500

Variables de entorno

AcciónCódigo
Guardarpm.environment.set("artistaId", value)
Leerpm.environment.get("artistaId")
Eliminarpm.environment.unset("artistaId")
Usar en URL/api/artistas/{{artistaId}}