Módulo 6: Checks, groups y escenarios realistas

1. Presentación del módulo: de golpear una ruta a un escenario

Descripción

Hasta el módulo 5 tu prueba de carga tenía una forma muy simple: muchos VUs golpeando una ruta con el mismo dato. POST /quote con Focus/basic/3h, repetido miles de veces. Con eso aprendiste a generar carga, a medir latencia y throughput, a poner umbrales que hacen pasar o fallar la prueba. Todo correcto. Pero si te paras a mirar lo que mediste, notarás algo incómodo: eso no se parece a lo que hacen los usuarios de verdad. Nadie cotiza la misma sala mil veces seguidas. La gente cotiza salas distintas, por horas distintas, con planes distintos; y una parte importante no se queda en la cotización, sino que reserva —un segundo paso que depende del resultado del primero—. Este módulo es el que cierra esa brecha. Es donde la prueba de carga deja de ser un martillo repetitivo y se convierte en un escenario que imita el uso real.

Para dar ese salto necesitas cuatro piezas nuevas, y este módulo instala una por una. La primera es usar check() para verificar la corrección —no solo que la API responda, sino que responda bien: status 200 y el price_cents correcto y el body con la forma esperada—. La segunda es group(), para organizar un script de varios pasos en bloques con nombre y obtener métricas por paso. La tercera es parametrizar los datos: dejar de pedir siempre lo mismo y variar sala/tier/horas con una lista de datos, para no golpear una sola ruta caliente. Y la cuarta, el corazón del módulo, es la correlación: extraer un valor de una respuesta y usarlo en la siguiente —el flujo cotizar → reservar → confirmar—. Se cierra con el think time realista, una pausa con jitter para que los usuarios virtuales no marchen todos al mismo compás.

Conexión con el módulo: esta lección es el mapa; no ejecuta todavía el escenario completo —eso empieza en la lección 2 y culmina en el proyecto de la 8—. Lo que instala aquí es la estructura y una decisión de infraestructura que declaro con todas las letras. El escenario multi-paso, con checks y correlación, se ejecuta de verdad en el generador de Python contra la API canónica de Reservo, y verás su salida real. Los constructos de k6 —check, group, SharedArray, la correlación con res.json('booking_id')— van como contenido rotulado, fieles a la documentación de k6.io, porque k6 no está instalado en este entorno. Todo lo que rotulemos como salida de Python fue medido aquí con Python 3.14.0.

Un guion de teatro, no una nota repetida

Piénsalo con una analogía. Hasta ahora tu prueba de carga era como un músico tocando una sola nota a todo volumen, mil veces por segundo. Con eso puedes medir cosas útiles: qué tan fuerte suena, si el amplificador aguanta el volumen, cuánto calienta el equipo. Pero nadie va a un concierto a escuchar una nota repetida. La música de verdad es una pieza: notas distintas, en una secuencia, donde cada compás depende del anterior, con silencios que le dan ritmo. Medir cómo aguanta el equipo tocando una pieza real —con su variedad y su dependencia entre partes— te dice algo que la nota repetida jamás podría.

Una prueba de carga realista es esa pieza. En vez de golpear /quote con el mismo dato, tu escenario cotiza una sala variada, reserva usando el precio que le cotizaron, y confirma usando el id que le devolvió la reserva —una secuencia donde cada paso depende del anterior—. Verifica en cada paso que la respuesta fue correcta (los check), organiza los pasos en bloques con nombre (los group), varía el dato de entrada en cada iteración (la parametrización), y hace pausas realistas entre pasos (el think time). El resultado no es "¿aguanta mil veces la misma nota?", sino "¿aguanta la pieza completa que de verdad van a tocar los usuarios?". Esa pregunta es mucho más útil, y responderla es de lo que trata este módulo.

Una prueba de carga realista no golpea una ruta con un dato fijo: ejecuta un escenario de varios pasos, con datos variados, donde cada paso depende del anterior (correlación), verifica la corrección en cada paso (checks), y hace pausas realistas (think time). Es la diferencia entre repetir una nota y tocar una pieza.

Las cuatro piezas de un escenario realista

Cada pieza tiene su nombre en k6 y su equivalente ejecutable en Python. Aquí está el mapa completo desde el minuto uno; cada lección desarrolla una fila.

PiezaQué aportaEn k6 (contenido)La ves ejecutada en Python
Check de correcciónVerificar que la respuesta es correcta, no solo que llegócheck(res, { ... }) con varios criteriosLecciones 2, 3, 6, 8
GroupOrganizar los pasos y medir por pasogroup('quote', () => { ... })Lección 4 (latencia por grupo)
ParametrizaciónVariar el dato para no golpear una ruta calienteSharedArray + índice aleatorioLección 5 (distribución real)
CorrelaciónExtraer un valor y reusarlo en el paso siguienteres.json('booking_id') → siguiente requestLecciones 6, 8
Think time con jitterPausa realista y desincronizada entre pasossleep(Math.random() * n + m)Lección 7 (con/sin, distribución)

Las cuatro se apoyan en lo que ya sabes. El check lo viste en su forma básica en el módulo 2; aquí lo pones a verificar corrección de verdad. El group y la parametrización son nuevos. La correlación es la pieza que convierte varios requests sueltos en un escenario con hilo. Y el think time lo has usado como sleep fijo; aquí le añades jitter. Ninguna es magia: todas se pueden escribir en veinte líneas de Python, y por eso las vas a ejecutar de verdad.

La declaración de este módulo: GET /booking/<id>

Aquí está la decisión de infraestructura, y la declaro abiertamente porque la regla de la guía es no esconder nada. La API canónica de Reservo —GET /rooms, POST /quote, POST /bookno cambia: sigue devolviendo 7500 para Focus/basic/3h y 6000 para Focus/pro/3h, idéntica a como la conociste. Pero el corazón de este módulo es la correlación, y para mostrarla completa necesito un tercer paso que use el booking_id que devuelve /book. Cotizar y reservar son dos pasos; un tercero que confirme la reserva por su id es lo que hace la correlación visible de punta a punta.

Por eso este módulo añade un endpoint al mismo servidor, y lo declaro con todas las letras:

Endpoint declaradoQué hacePara qué lo usamos
GET /booking/<id>Consulta una reserva ya creada por su booking_id y devuelve sus datos (booking_id, confirmed, price_cents, sala, tier, horas), o 404 si no existe.El tercer paso del escenario de correlación: usar el booking_id que devolvió /book para confirmar que la reserva existe.

Es un endpoint honesto: guarda cada reserva que /book crea en una tabla en memoria y la devuelve cuando la consultas por id. Con él, el flujo cotizar → reservar → confirmar queda completo y la correlación se ve de verdad: el booking_id que sale del paso 2 entra en la URL del paso 3, y si estuviera mal (un id inventado), el paso 3 devolvería 404 y el check fallaría. La API canónica no se toca; solo se le añade este endpoint de consulta, declarado aquí.

Una nota más sobre /book, también declarada: en este módulo POST /book acepta opcionalmente el price_cents que viste en la cotización, para poder mostrar la primera correlación (el precio de /quote viaja a /book). Si lo mandas y ya no coincide con el precio vigente, la API responde 409 —un escenario realista de "el precio cambió mientras reservabas"—. Si no lo mandas, reserva igual. Es una extensión aditiva sobre el /book canónico, no un cambio de su contrato.

El mapa de las ocho lecciones

Este módulo va de verificar bien una respuesta a encadenar varios pasos con datos variados a ponerlo todo junto en un escenario realista sobre Reservo. Cada lección deja una pieza:

LecciónQué instala
1. Presentación (esta)Las cuatro piezas de un escenario; el endpoint declarado; el mapa
2. Check de correcciónVerificar status y valor y forma del body bajo carga
3. Check que falla vs threshold que abortaEl check mide y sigue; el threshold da el veredicto
4. group()Organizar los pasos y medir por grupo (contenido)
5. Parametrizar datosVariar sala/tier/horas para no golpear una ruta caliente
6. CorrelaciónExtraer y reusar: cotizar → reservar → confirmar
7. Think time con jitterLa pausa realista y por qué se desincroniza
8. Mini-proyectoEl escenario completo, ejecutado, con la tasa de checks

En la guía entera, este módulo es donde la prueba se vuelve realista. Los módulos 2 a 5 te dieron la maquinaria —el script, las métricas, los perfiles, los thresholds—; este la pone al servicio de un escenario que imita el uso real. Y prepara el módulo 7, donde analizarás los resultados de una prueba así y la correrás en CI: para analizar un escenario de varios pasos, primero tienes que saber construirlo, y eso es lo que instalas aquí.

La frontera: qué es de este módulo y qué de los otros

Para que no mezcles, aquí está la línea exacta, porque es fácil querer arrastrar aquí cosas que ya tienen su casa:

TemaDónde vivePor qué no aquí
Checks, groups, parametrización, correlación, think timeEste módulo (6)
La anatomía del script y los VUsMódulo 2 (ya visto)Aquí los reusamos para construir el escenario
Las métricas (p95, RPS, tasa de error)Módulo 3 (ya visto)El escenario las produce; medirlas a fondo fue el 3
Los perfiles de carga (stages, rampas)Módulo 4 (ya visto)Cómo sube y baja la carga; aquí la carga es plana y variamos el contenido
Los thresholds (veredicto pasa/falla)Módulo 5 (reusado en L3)Aquí el check mide; el threshold decide —lo reusamos, no lo reexplicamos
Analizar resultados y correr en CIMódulo 7El "después" de tener un escenario que corre
El capstone (prueba de carga completa)Módulo 8Junta todo; aquí solo la pieza de escenario

La regla mecánica: si la pregunta es "¿cómo hago que mi prueba se parezca al uso real —varios pasos, datos variados, dependencia entre pasos, verificación de corrección?", es este módulo. Si es "¿cómo sube y baja la carga?", es el 4. Si es "¿cómo hago que la prueba falle cuando algo se rompe?", es el 5 (y lo reusamos en la lección 3). Si es "¿qué hago con los resultados y cómo lo corro en CI?", es el 7.

Errores comunes

Creer que golpear una ruta con un dato fijo ya es "una prueba de carga realista". Qué pasa: alguien lanza mil VUs contra /quote con Focus/basic/3h y concluye "la API aguanta". Por qué pasa: se confunde generar volumen con generar uso realista. Cómo detectarlo: si todos tus VUs piden exactamente lo mismo y nunca encadenan un segundo paso, estás midiendo un camino, no el sistema. Cómo corregirlo: parametriza los datos (lección 5) y encadena los pasos con correlación (lección 6). Un usuario real cotiza cosas distintas y muchas veces reserva.

Verificar solo que la API "respondió" y no que respondió bien. Qué pasa: alguien pone check(res, { 'status is 200': ... }) y con eso se da por satisfecho. Por qué pasa: un 200 se siente como "todo bien". Cómo detectarlo: bajo estrés, la API puede responder 200 con un precio equivocado o un body malformado, y tu prueba no lo notaría. Cómo corregirlo: verifica también el valor (price_cents correcto) y la forma (que la clave exista y sea del tipo esperado). Es justo la lección 2.

Inventar el dato del segundo paso en vez de correlacionarlo. Qué pasa: alguien quiere probar el flujo cotizar → reservar → confirmar, pero en el paso de confirmar usa un booking_id fijo o inventado en vez del que devolvió la reserva. Por qué pasa: parece más fácil "hardcodear" un id. Cómo detectarlo: el paso de confirmar falla siempre (o peor, pasa por casualidad contra un id que existe por otra razón), y no estás probando el flujo real. Cómo corregirlo: extrae el booking_id de la respuesta de /book y úsalo en el paso siguiente. Eso es correlación, y es la lección 6.

Ejercicios

Ejercicio 1 — ¿Qué pieza resuelve cada problema? Para cada síntoma de una prueba de carga poco realista, di qué pieza del módulo lo arregla (check de corrección, group, parametrización, correlación o think time). (a) "Todos mis VUs piden la misma sala, así que mido solo esa ruta." (b) "Mi prueba dice 200 pero no sé si el precio era correcto." (c) "Quiero probar el flujo completo cotizar → reservar, pero no sé cómo pasar el id de un paso al otro." (d) "Todos mis VUs piden exactamente al mismo tiempo, en picos artificiales."

Ver solución
  • (a) Parametrización (lección 5): variar sala/tier/horas con una lista de datos para no golpear una ruta caliente.
  • (b) Check de corrección (lección 2): verificar no solo el status sino el valor (price_cents correcto) y la forma del body.
  • (c) Correlación (lección 6): extraer el booking_id de la respuesta de /book y usarlo en el paso siguiente.
  • (d) Think time con jitter (lección 7): una pausa aleatoria que desincroniza a los VUs y evita picos artificiales.

Ejercicio 2 — ¿Por qué se declara GET /booking/<id>? En una o dos frases: (a) ¿Por qué este módulo añade un endpoint de consulta de reservas en vez de quedarse con /quote y /book? (b) ¿Qué pasaría en el paso de confirmar si el booking_id que usa el escenario estuviera mal (inventado)?

Ver solución
  • (a) Porque el corazón del módulo es la correlación, y para mostrarla completa hace falta un tercer paso que use el booking_id devuelto por /book. GET /booking/<id> es ese paso: consulta la reserva por el id que salió del paso anterior, cerrando el flujo cotizar → reservar → confirmar.
  • (b) El endpoint devolvería 404 (la reserva no existe), y el check del paso de confirmar (status is 200, id matches) fallaría. Eso es justo lo que hace útil la correlación: usar el id real que devolvió la reserva, no uno inventado, es lo que hace que el paso siguiente funcione.

Ejercicio 3 — La frontera. Un compañero dice, sobre su prueba de carga de Reservo: (a) "Quiero que la carga suba en rampa de 0 a 50 VUs." (b) "Quiero que la prueba falle si el p95 pasa de 500 ms." (c) "Quiero que cada VU cotice una sala distinta y luego reserve con el precio que le cotizaron." ¿Cuáles de estas tres son tema de este módulo y cuáles no? Nombra el módulo correcto para las que no.

Ver solución
  • (a) No es de este módulo: subir la carga en rampa son los perfiles de carga (stages, ramping-vus), tema del módulo 4.
  • (b) No es de este módulo: hacer que la prueba falle al superar un umbral es un threshold, tema del módulo 5 (aquí lo reusamos en la lección 3, pero se enseña allá).
  • (c) es de este módulo: cotizar una sala distinta es parametrización (lección 5) y reservar con el precio cotizado es correlación (lección 6). Es exactamente el escenario que construimos aquí.

Resumen y siguiente paso

En esta lección montaste el mapa del módulo. Una prueba de carga realista deja de golpear una ruta con un dato fijo y ejecuta un escenario: varios pasos donde cada uno depende del anterior. Cuatro piezas lo hacen posible —el check de corrección (verificar que la respuesta es correcta, no solo que llegó), el group() (organizar y medir por paso), la parametrización (variar el dato para no golpear una ruta caliente) y la correlación (extraer un valor y reusarlo en el paso siguiente)— más el think time con jitter para un ritmo realista. Es la diferencia entre repetir una nota y tocar una pieza.

Conociste la declaración de este módulo: sobre la misma API canónica de Reservo (que sigue devolviendo 7500 y 6000, intacta), se añade GET /booking/<id> —consultar una reserva por su id— para que el tercer paso del flujo de correlación sea real; y POST /book acepta ahora, aditivamente, el price_cents cotizado para mostrar la primera correlación. Y quedó clara la frontera: aquí se construye el escenario realista; los perfiles (M4), los thresholds (M5, reusado en L3) y el análisis + CI (M7) tienen su casa aparte.

Antes de avanzar deberías poder: nombrar las cuatro piezas de un escenario realista y qué aporta cada una; explicar por qué golpear una ruta con un dato fijo no basta; y decir con tus palabras qué es la correlación y por qué el módulo declara GET /booking/<id>. Lo que sigue es empezar a construir. En la lección 2 pones check() a trabajar de verdad: no solo verificar que la API respondió, sino que respondió bien —status 200 y price_cents correcto y el body con la forma esperada—, y lo mides ejecutando tres checks por respuesta contra la API canónica.

Recursos

  • Escenarios y flujos de usuario en k6 — la referencia oficial de cómo k6 modela escenarios de usuario de varios pasos, el marco de todo este módulo. Cómo se estructura una prueba que imita el uso real.
  • Checks en k6 — la pieza de verificación que la lección 2 pone a trabajar para la corrección. La base de todo el módulo.
  • Módulo 5 de esta guía — Thresholds, pasa/falla y SLOs — el veredicto pasa/falla que la lección 3 reúsa para contrastar con el check. Si no tienes claro qué es un threshold, repásalo.
  • http.server — documentación de Python — el servidor de la biblioteca estándar con el que corre la API canónica de Reservo, incluido el endpoint GET /booking/<id> declarado en este módulo. Cómo se levanta el blanco de carga.