Qué significa “datos de prueba estables” en Postman
En la práctica, la estabilidad de ejecuciones (baja flakiness) depende de que los datos usados por tus requests sean predecibles, aislados y trazables. Esto implica: evitar colisiones (dos ejecuciones creando el mismo “nombre”), controlar el tiempo (fechas consistentes), limpiar recursos creados (teardown) y registrar evidencia (IDs, respuestas y logs) para auditoría y depuración.
Estrategias operativas de datos de prueba (implementación técnica)
1) Datasets controlados (semilla conocida)
Un dataset controlado es un conjunto de valores “congelados” que se reutilizan de forma consistente. Es útil para pruebas que validan reglas de negocio sin depender de generación aleatoria. En Postman puedes implementarlo con variables de colección o de entorno, o con un objeto JSON en una variable.
Ejemplo: dataset en variable de colección
// En una variable de colección llamada testData (tipo string JSON) guarda algo como: {"user":{"name":"qa_user","emailDomain":"example.test"},"product":{"sku":"SKU-BASE-001"}}Lectura del dataset en scripts
const testData = JSON.parse(pm.collectionVariables.get('testData'));Recomendación: usa datasets controlados para entidades “base” (catálogos, SKUs, roles, permisos) y combina con generación dinámica solo para los campos que deben ser únicos.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
2) Generación dinámica (unicidad y aislamiento por ejecución)
La generación dinámica reduce colisiones cuando varias personas o pipelines ejecutan la misma colección. La idea es crear un runId único por ejecución y derivar de él nombres, correos y referencias.
Paso a paso: crear un runId único una sola vez
- En el primer request del flujo (o en un “Setup” dedicado), en
Pre-request Script, inicializarunIdsi no existe. - Guarda el
runIden variable de colección (para que sobreviva a toda la colección) o en entorno (si necesitas compartirlo entre colecciones).
if (!pm.collectionVariables.get('runId')) { const runId = `${Date.now()}-${pm.info.iteration}-${pm.info.requestName}`; pm.collectionVariables.set('runId', runId);}Construir valores únicos
const runId = pm.collectionVariables.get('runId');const uniqueName = `pm-${runId}`;const uniqueEmail = `user+${runId}@example.test`;pm.collectionVariables.set('uniqueName', uniqueName);pm.collectionVariables.set('uniqueEmail', uniqueEmail);Notas prácticas: Date.now() suele ser suficiente; si ejecutas en paralelo y quieres más entropía, agrega un random corto.
const rand = Math.random().toString(16).slice(2, 8);const runId = `${Date.now()}-${rand}`;3) Limpieza (teardown) para no contaminar el entorno
El teardown consiste en borrar (o revertir) los recursos creados durante la ejecución. Esto evita que el dataset “crezca” indefinidamente y reduce fallos por límites, duplicados o reglas de unicidad.
Patrón recomendado: cada vez que crees un recurso, guarda su ID en una lista; al final, recorre la lista y elimina.
Control de colisiones: IDs y nombres únicos sin fricción
Guardar IDs relevantes para auditoría y teardown
Cuando un request crea un recurso, captura el ID desde el response y guárdalo. Además, registra el ID en una lista de “recursos a borrar”.
// Tests tab del request que crea un recursoconst json = pm.response.json();const id = json.id || json.data?.id;pm.test('Se obtuvo id', () => pm.expect(id).to.exist);pm.collectionVariables.set('lastCreatedId', id);// Lista para teardown (array JSON en string)const key = 'createdResourceIds';const current = JSON.parse(pm.collectionVariables.get(key) || '[]');current.push(id);pm.collectionVariables.set(key, JSON.stringify(current));Si creas distintos tipos de recursos, guarda un registro estructurado (tipo y id) para borrar con el endpoint correcto.
const key = 'createdResources';const current = JSON.parse(pm.collectionVariables.get(key) || '[]');current.push({ type: 'user', id });pm.collectionVariables.set(key, JSON.stringify(current));Evitar colisiones en entornos compartidos
En entornos compartidos (mismo ambiente para varios testers), el riesgo principal es que dos ejecuciones usen la misma variable “global” o “de entorno” y se pisen. Para minimizarlo:
- Prefiere variables de colección para datos de una ejecución (aisladas al run de la colección).
- Si debes escribir en entorno compartido, usa claves “namespaced” por
runId(por ejemplo,userId__<runId>), o guarda un objeto JSON con mapa por runId. - No reutilices variables genéricas como
userIdsi hay concurrencia; usauserId_currentRuno un mapa.
Ejemplo: mapa por runId en una sola variable
const runId = pm.collectionVariables.get('runId');const key = 'runState';const state = JSON.parse(pm.environment.get(key) || '{}');state[runId] = state[runId] || {};state[runId].userId = pm.collectionVariables.get('lastCreatedId');pm.environment.set(key, JSON.stringify(state));Control de fechas y tiempo para reducir flakiness
Fechas deterministas (congelar “hoy”)
Muchos fallos intermitentes vienen de zonas horarias, cambios de día durante la ejecución o validaciones sensibles al tiempo. Una técnica simple es fijar un “now” al inicio y derivar todas las fechas desde ahí.
Paso a paso
- En un Setup, fija
baseNowen ISO. - Deriva fechas relativas (por ejemplo, +7 días) desde
baseNow.
if (!pm.collectionVariables.get('baseNow')) { pm.collectionVariables.set('baseNow', new Date().toISOString());}const baseNow = new Date(pm.collectionVariables.get('baseNow'));const plusDays = (d, days) => { const x = new Date(d.getTime()); x.setUTCDate(x.getUTCDate() + days); return x.toISOString();};pm.collectionVariables.set('dateToday', baseNow.toISOString());pm.collectionVariables.set('datePlus7', plusDays(baseNow, 7));Recomendación: usa UTC (métodos setUTCDate, getUTCDate) para evitar diferencias por zona horaria.
Evitar aserciones frágiles con timestamps
Si el API devuelve timestamps generados por el servidor, evita comparar igualdad exacta. En su lugar, valida formato y rango.
const json = pm.response.json();pm.test('createdAt es ISO', () => { pm.expect(json.createdAt).to.match(/^\d{4}-\d{2}-\d{2}T/);});pm.test('createdAt no es futuro lejano', () => { const t = new Date(json.createdAt).getTime(); const now = Date.now(); pm.expect(t).to.be.below(now + 5 * 60 * 1000); // 5 min tolerancia});Minimizar flakiness: prácticas técnicas en requests y scripts
1) Reintentos controlados (solo cuando aplica)
Si tu sistema tiene consistencia eventual (por ejemplo, un recurso tarda en estar disponible), puedes implementar un reintento limitado. En Postman, una forma práctica es re-ejecutar el request actual con postman.setNextRequest y un contador.
// Tests tab del request que consulta disponibilidadconst maxRetries = 3;const retryKey = 'retryCount';const retryCount = Number(pm.collectionVariables.get(retryKey) || 0);if (pm.response.code === 404 && retryCount < maxRetries) { pm.collectionVariables.set(retryKey, retryCount + 1); postman.setNextRequest(pm.info.requestName); // reintenta el mismo request} else { pm.collectionVariables.unset(retryKey);}Úsalo con cuidado: reintentar todo oculta problemas reales. Limítalo a endpoints conocidos por consistencia eventual.
2) Aislar dependencias: no reutilizar recursos “mutables”
Evita que múltiples ejecuciones modifiquen el mismo recurso (por ejemplo, el mismo usuario). Si necesitas un recurso base, clónalo por ejecución (con nombre único) o crea uno nuevo y bórralo al final.
3) Validaciones robustas: menos igualdad exacta, más invariantes
Para estabilidad, prioriza invariantes: presencia de campos, tipos, rangos, estados válidos, y relaciones (por ejemplo, el ID del response coincide con el ID solicitado). Evita aserciones sobre orden de arrays si no está garantizado.
Teardown básico: borrar recursos creados
Opción A: request(s) dedicados de limpieza al final
Crea un folder “Teardown” al final de la colección con requests de borrado. Cada request toma IDs desde la lista guardada.
Ejemplo: borrar usuarios desde lista
// Pre-request Script del request DELETE /users/:idconst resources = JSON.parse(pm.collectionVariables.get('createdResources') || '[]');const next = resources.find(r => r.type === 'user');if (!next) { postman.setNextRequest(null); // o salta al siguiente teardown return;}pm.collectionVariables.set('deleteUserId', next.id);// Tests tab: tras borrar, remover de la lista y continuarconst resources = JSON.parse(pm.collectionVariables.get('createdResources') || '[]');const id = pm.collectionVariables.get('deleteUserId');const remaining = resources.filter(r => !(r.type === 'user' && String(r.id) === String(id)));pm.collectionVariables.set('createdResources', JSON.stringify(remaining));postman.setNextRequest(pm.info.requestName); // repite hasta vaciarVentaja: simple y explícito. Consideración: requiere endpoints de borrado disponibles y permisos adecuados.
Opción B: teardown “best-effort” (no fallar la suite por limpieza)
Si el objetivo principal es validar funcionalidad, la limpieza puede ser “best-effort”: registra si falló, pero no rompas toda la ejecución. En tests, evita pm.test estrictos para el teardown o marca advertencias en consola.
if (pm.response.code !== 200 && pm.response.code !== 204 && pm.response.code !== 404) { console.warn('Teardown: respuesta inesperada', pm.response.code, pm.response.text());}Manejo de entornos compartidos: reglas prácticas
| Problema | Implementación recomendada |
|---|---|
| Variables pisadas por ejecuciones simultáneas | Guardar estado por runId (mapa JSON) o usar variables de colección para estado efímero |
| Datos “huérfanos” por ejecuciones interrumpidas | Teardown al final + endpoint de limpieza por prefijo (si existe) usando runId en nombres |
| Auditoría difícil (no se sabe qué creó cada run) | Registrar runId, IDs creados y timestamps en una variable auditLog |
Bitácora de auditoría en una variable
Guarda un log estructurado por ejecución con eventos clave (creación/borrado, IDs, endpoints). Esto ayuda a reproducir y explicar resultados.
const runId = pm.collectionVariables.get('runId');const key = 'auditLog';const log = JSON.parse(pm.collectionVariables.get(key) || '[]');log.push({ ts: new Date().toISOString(), runId, request: pm.info.requestName, method: pm.request.method, url: pm.request.url.toString(), status: pm.response.code, ids: { lastCreatedId: pm.collectionVariables.get('lastCreatedId') }});pm.collectionVariables.set(key, JSON.stringify(log));Registro de evidencia: respuestas, consola y almacenamiento de IDs
1) Capturar “snapshot” del response (sanitizado)
Para evidencia, guarda una versión acotada del response (por ejemplo, solo campos relevantes) en una variable. Evita almacenar tokens o datos sensibles.
const json = pm.response.json();const snapshot = { id: json.id, status: json.status, createdAt: json.createdAt};pm.collectionVariables.set('lastResponseSnapshot', JSON.stringify(snapshot));2) Logs en consola con contexto
Usa console.log para imprimir runId, IDs y decisiones (reintentos, saltos). Esto acelera el diagnóstico cuando un run falla en CI.
console.log('runId=', pm.collectionVariables.get('runId'));console.log('createdResources=', pm.collectionVariables.get('createdResources'));3) Persistir IDs clave para revisión
Además de la lista para teardown, guarda IDs “de negocio” (por ejemplo, orderId, invoiceId) en variables con nombre claro. Si hay múltiples, usa arrays.
const key = 'orderIds';const arr = JSON.parse(pm.collectionVariables.get(key) || '[]');arr.push(pm.collectionVariables.get('lastCreatedId'));pm.collectionVariables.set(key, JSON.stringify(arr));Guía práctica: plantilla operativa para una ejecución estable
Paso 1: Setup (una vez por ejecución)
- Inicializa
runIdybaseNow. - Carga
testData(dataset controlado) y deriva valores únicos. - Inicializa contenedores:
createdResources,auditLog.
if (!pm.collectionVariables.get('runId')) { pm.collectionVariables.set('runId', `${Date.now()}-${Math.random().toString(16).slice(2,8)}`);}if (!pm.collectionVariables.get('baseNow')) { pm.collectionVariables.set('baseNow', new Date().toISOString());}if (!pm.collectionVariables.get('createdResources')) { pm.collectionVariables.set('createdResources', '[]');}if (!pm.collectionVariables.get('auditLog')) { pm.collectionVariables.set('auditLog', '[]');}const runId = pm.collectionVariables.get('runId');pm.collectionVariables.set('uniqueEmail', `user+${runId}@example.test`);pm.collectionVariables.set('uniqueName', `pm-${runId}`);Paso 2: En cada creación, guardar ID y registrar evidencia
- Extrae ID del response.
- Agrega a
createdResourcescon tipo. - Guarda snapshot y agrega entrada a
auditLog.
Paso 3: En lecturas/validaciones sensibles al tiempo
- Usa
baseNowpara fechas en requests. - Valida timestamps por formato/rango, no por igualdad exacta.
Paso 4: Teardown al final
- Recorre
createdResourcesy borra en orden seguro (por ejemplo, primero hijos, luego padres). - Teardown best-effort si el objetivo es no bloquear el pipeline por limpieza.