Qué es un test en Postman y qué problema resuelve
En Postman, un test es un script (JavaScript) que se ejecuta después de recibir la respuesta de una request. Su objetivo es convertir una verificación manual (“se ve bien”) en aserciones repetibles sobre: código de estado, tiempos, headers, estructura y contenido del JSON, y reglas de negocio. Cuando estos tests se ejecutan en Collection Runner o en CI, actúan como una red de seguridad para detectar regresiones.
Guía práctica: crear aserciones esenciales en la pestaña Tests
1) Aserciones de status code
Valida que el endpoint responda con el código esperado. Esto detecta cambios de comportamiento (por ejemplo, un 200 que pasa a 204 o un 201 que se convierte en 200).
pm.test("Status code es 200", function () { pm.response.to.have.status(200);});Si tu API usa códigos diferentes según operación, crea tests específicos por request (por ejemplo, 201 en creación, 204 en borrado).
2) Aserciones de tiempo de respuesta
El tiempo no solo es performance: también ayuda a detectar degradaciones o problemas de infraestructura. Define umbrales realistas por entorno.
pm.test("Tiempo de respuesta < 800ms", function () { pm.expect(pm.response.responseTime).to.be.below(800);});3) Aserciones de headers
Verifica headers críticos del contrato (por ejemplo, tipo de contenido, caching, correlación). Un caso típico es asegurar JSON.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
pm.test("Content-Type es JSON", function () { pm.response.to.have.header("Content-Type"); pm.expect(pm.response.headers.get("Content-Type")).to.match(/application\/json/i);});Si tu API usa un header de trazabilidad (por ejemplo X-Request-Id), valida su presencia y formato.
pm.test("Incluye X-Request-Id", function () { const rid = pm.response.headers.get("X-Request-Id"); pm.expect(rid, "X-Request-Id debe existir").to.be.a("string").and.not.be.empty;});4) Aserciones sobre contenido JSON
Primero, asegúrate de que la respuesta sea JSON parseable y luego valida campos.
pm.test("Body es JSON válido", function () { pm.response.to.be.json;});const body = pm.response.json();Ejemplo: validar que exista un objeto data y que tenga un id.
pm.test("data.id existe", function () { const body = pm.response.json(); pm.expect(body).to.have.property("data"); pm.expect(body.data).to.have.property("id");});Validaciones de contrato a nivel de campos: obligatorios, tipos, rangos y reglas simples
Estas validaciones son el núcleo de la “calidad funcional” del contrato. No solo verifican que el endpoint responda, sino que responda con datos coherentes.
1) Campos obligatorios (required)
Define qué campos deben existir siempre. Si un campo es opcional, no lo marques como obligatorio: en su lugar, valida su tipo solo si existe.
pm.test("Campos obligatorios presentes", function () { const b = pm.response.json(); const required = ["id", "name", "status"]; required.forEach((k) => { pm.expect(b.data, `Falta campo obligatorio: ${k}`).to.have.property(k); });});2) Tipos de datos
Valida tipos para evitar cambios silenciosos (por ejemplo, id de número a string). Postman usa Chai (pm.expect).
pm.test("Tipos correctos", function () { const d = pm.response.json().data; pm.expect(d.id, "id debe ser number").to.be.a("number"); pm.expect(d.name, "name debe ser string").to.be.a("string"); pm.expect(d.active, "active debe ser boolean").to.be.a("boolean");});3) Rangos y límites
Útil para montos, edades, porcentajes, paginación, etc.
pm.test("Rangos válidos", function () { const d = pm.response.json().data; pm.expect(d.score, "score debe estar entre 0 y 100").to.be.within(0, 100); pm.expect(d.itemsCount, "itemsCount no puede ser negativo").to.be.at.least(0);});4) Reglas de negocio simples
Ejemplos típicos: consistencia entre campos, estados permitidos, fechas lógicas.
pm.test("Reglas de negocio: status permitido y consistencia", function () { const d = pm.response.json().data; pm.expect(d.status).to.be.oneOf(["NEW", "ACTIVE", "SUSPENDED"]); if (d.status === "SUSPENDED") { pm.expect(d.suspensionReason, "Si está SUSPENDED debe haber motivo").to.be.a("string").and.not.be.empty; }});Para fechas, valida formato y orden (sin depender de librerías externas).
pm.test("Fechas coherentes", function () { const d = pm.response.json().data; const created = Date.parse(d.createdAt); const updated = Date.parse(d.updatedAt); pm.expect(created, "createdAt debe ser parseable").to.not.be.NaN; pm.expect(updated, "updatedAt debe ser parseable").to.not.be.NaN; pm.expect(updated, "updatedAt debe ser >= createdAt").to.be.at.least(created);});Validación de contratos con JSON Schema (JSON Schema Validation)
Las aserciones por campo son claras, pero escalan mal cuando el payload crece. La validación por JSON Schema permite describir la forma del JSON (tipos, required, enums, formatos) y validar la respuesta completa contra ese contrato.
1) Preparar el esquema por recurso
Recomendación práctica: mantener un esquema por recurso/endpoint (por ejemplo, User.json, Order.json, Error.json) y versionarlos junto con la colección. En Postman puedes guardarlos como:
- Variables de colección:
schema_user,schema_order, etc. (cómodo para reutilizar en requests de la misma colección). - Archivos en el repo y cargarlos en CI (si tu pipeline inyecta el contenido como variables). En Postman UI, lo más directo es colección/entorno.
Ejemplo de esquema mínimo para data (guardado en una variable de colección schema_user):
{ "type": "object", "required": ["data"], "properties": { "data": { "type": "object", "required": ["id", "name", "status"], "properties": { "id": {"type": "number"}, "name": {"type": "string", "minLength": 1}, "status": {"type": "string", "enum": ["NEW", "ACTIVE", "SUSPENDED"]}, "active": {"type": "boolean"} }, "additionalProperties": true } }, "additionalProperties": true}2) Validar la respuesta contra el esquema
Postman incluye tv4 en muchos entornos de ejecución clásicos; en otros casos, se usa Ajv como librería externa, pero aquí nos enfocamos en lo que suele estar disponible sin dependencias. Primero, recupera el esquema desde variables y valida.
pm.test("Contrato JSON Schema: User", function () { const schemaRaw = pm.collectionVariables.get("schema_user"); pm.expect(schemaRaw, "No existe schema_user en variables de colección").to.be.a("string").and.not.be.empty; const schema = JSON.parse(schemaRaw); const data = pm.response.json(); const result = tv4.validateMultiple(data, schema); if (!result.valid) { const details = result.errors.map(e => `Ruta: ${e.dataPath || '/'} | Regla: ${e.message}`).join("; "); pm.expect.fail(`Fallo de esquema: ${details}`); }});Notas prácticas:
validateMultiplepermite listar múltiples errores, lo que acelera el diagnóstico.- Usa
pm.expect.failpara que el reporte muestre un mensaje claro y accionable. - Si tu API envuelve respuestas (por ejemplo
{ data, meta }), el esquema debe reflejar ese wrapper.
3) Mantener esquemas por recurso (estrategia de organización)
| Enfoque | Cómo se guarda | Cuándo conviene |
|---|---|---|
| Esquema por request | Variable local en el script | Prototipos rápidos, payload pequeño |
| Esquema por recurso | Variables de colección (schema_user, schema_order) | Reutilización entre requests (GET/POST/PUT del mismo recurso) |
| Esquema por versión | schema_user_v1, schema_user_v2 | APIs versionadas o migraciones graduales |
| Esquema de error común | schema_error | Estandarizar respuestas 4xx/5xx |
Consejo: evita duplicar esquemas casi iguales. Si hay pequeñas variaciones (por ejemplo, en POST el id puede no venir), crea esquemas específicos: schema_user_get y schema_user_create_response.
Casos negativos: validar errores esperados (4xx/5xx) y consistencia
Probar “lo que sale bien” no es suficiente. Los casos negativos garantizan que el sistema falle de forma predecible y con mensajes consistentes para clientes.
1) Aserción de status code de error esperado
Ejemplo: request con parámetro inválido debe devolver 400.
pm.test("Error esperado: 400", function () { pm.response.to.have.status(400);});2) Contrato de error consistente (código interno, mensaje, detalles)
Define un formato estándar de error, por ejemplo:
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid input", "details": [ {"field": "email", "issue": "invalid"} ] }}Valida estructura y tipos:
pm.test("Contrato de error consistente", function () { const b = pm.response.json(); pm.expect(b).to.have.property("error"); pm.expect(b.error).to.have.property("code").that.is.a("string"); pm.expect(b.error).to.have.property("message").that.is.a("string"); pm.expect(b.error).to.have.property("details"); pm.expect(b.error.details).to.be.an("array");});Valida códigos internos (útil para soporte y trazabilidad):
pm.test("Código interno de error permitido", function () { const code = pm.response.json().error.code; pm.expect(code).to.be.oneOf(["VALIDATION_ERROR", "NOT_FOUND", "UNAUTHORIZED", "FORBIDDEN", "CONFLICT", "INTERNAL_ERROR"]);});3) Validación por JSON Schema para errores
Guarda un schema_error y reutilízalo en todas las requests negativas.
pm.test("JSON Schema: Error", function () { const schema = JSON.parse(pm.collectionVariables.get("schema_error")); const result = tv4.validateMultiple(pm.response.json(), schema); if (!result.valid) { pm.expect.fail("Fallo de esquema (error): " + result.errors.map(e => e.message).join(" | ")); }});4) Manejo de 5xx esperados en entornos de prueba
A veces se prueban fallos controlados (por ejemplo, dependencia caída simulada). Si esperas un 503, valida que el error sea informativo y consistente (sin filtrar stack traces).
pm.test("503 controlado sin fuga de información", function () { pm.response.to.have.status(503); const raw = pm.response.text(); pm.expect(raw).to.not.match(/Exception|StackTrace|NullPointer/i);});Mensajes de fallo claros: hacer que el reporte sea accionable
Un test útil no solo falla: explica qué falló y dónde. Usa mensajes en pm.expect y construye detalles cuando iteras arrays.
pm.test("Validación de lista: cada item tiene id y name", function () { const items = pm.response.json().data.items; pm.expect(items, "data.items debe ser array").to.be.an("array"); items.forEach((it, idx) => { pm.expect(it.id, `items[${idx}].id requerido`).to.exist; pm.expect(it.name, `items[${idx}].name requerido`).to.be.a("string").and.not.be.empty; });});Set de tests base reutilizable para estandarizar calidad
Para no repetir lógica en cada request, crea un “pack” de tests base que puedas copiar/pegar y ajustar con variables. La idea es que todas las requests compartan un mínimo de calidad: status, tiempo, headers, JSON válido y (si aplica) esquema.
Plantilla: tests base para respuestas exitosas JSON
Configura variables de colección por request (o por carpeta) para parametrizar:
expected_status(ej. 200, 201)max_response_time_ms(ej. 800)schema_key(ej.schema_user,schema_order)
// ===== Tests base reutilizables (Success JSON) =====const expectedStatus = Number(pm.collectionVariables.get("expected_status") || 200);const maxTime = Number(pm.collectionVariables.get("max_response_time_ms") || 1000);const schemaKey = pm.collectionVariables.get("schema_key");pm.test(`Status code es ${expectedStatus}`, function () { pm.response.to.have.status(expectedStatus);});pm.test(`Tiempo de respuesta < ${maxTime}ms`, function () { pm.expect(pm.response.responseTime).to.be.below(maxTime);});pm.test("Content-Type es JSON", function () { pm.response.to.have.header("Content-Type"); pm.expect(pm.response.headers.get("Content-Type")).to.match(/application\/json/i);});pm.test("Body es JSON válido", function () { pm.response.to.be.json;});if (schemaKey) { pm.test(`JSON Schema válido: ${schemaKey}`, function () { const schemaRaw = pm.collectionVariables.get(schemaKey); pm.expect(schemaRaw, `No existe variable de colección: ${schemaKey}`).to.be.a("string").and.not.be.empty; const schema = JSON.parse(schemaRaw); const result = tv4.validateMultiple(pm.response.json(), schema); if (!result.valid) { const details = result.errors.map(e => `Ruta: ${e.dataPath || '/'} | ${e.message}`).join("; "); pm.expect.fail(`Fallo de esquema (${schemaKey}): ${details}`); } });}Plantilla: tests base para errores esperados
Parametriza el status de error y el esquema de error común.
// ===== Tests base reutilizables (Expected Error) =====const expectedErrorStatus = Number(pm.collectionVariables.get("expected_error_status") || 400);const errorSchemaKey = pm.collectionVariables.get("error_schema_key") || "schema_error";pm.test(`Status code de error es ${expectedErrorStatus}`, function () { pm.response.to.have.status(expectedErrorStatus);});pm.test("Contrato de error: JSON válido", function () { pm.response.to.be.json;});pm.test(`JSON Schema válido (error): ${errorSchemaKey}`, function () { const schemaRaw = pm.collectionVariables.get(errorSchemaKey); pm.expect(schemaRaw, `No existe variable de colección: ${errorSchemaKey}`).to.be.a("string").and.not.be.empty; const schema = JSON.parse(schemaRaw); const result = tv4.validateMultiple(pm.response.json(), schema); if (!result.valid) { pm.expect.fail("Fallo de esquema (error): " + result.errors.map(e => e.message).join(" | ")); }});pm.test("Código interno de error presente", function () { const b = pm.response.json(); pm.expect(b).to.have.property("error"); pm.expect(b.error).to.have.property("code").that.is.a("string").and.not.be.empty;});Con estas plantillas, puedes estandarizar rápidamente: cada request define sus variables (status esperado, umbral de tiempo, esquema) y hereda el mismo nivel de exigencia. Esto reduce la variabilidad entre colecciones y hace que los reportes sean comparables entre equipos y servicios.