⭐ 0/6
1 / 28
← → navegar · N notas presentador · F pantalla completa
🎙 Notas del presentador
QA Engineering · Automatización de pruebas

API Automation con Playwright

Probar el backend directo — sin navegador, sin clics.
La fixture request · aserciones de respuesta · validación de schema · KATA para API.

request fixturestatus + bodyschemaKATA · ApiBaserunner real6 quizzes ⭐

Sesión de API automation con Playwright. Prerequisito: ya entienden tests, clases y fixtures básicas (sesión Automation Patterns). Hoy bajamos del navegador a la capa HTTP: probar endpoints directo. Misma promesa pedagógica: primero el dolor, luego la herramienta. Todo en vivo — un runner que ejecuta JavaScript real contra una API falsa en memoria, un trazador del ciclo de vida de una request, un simulador que compara velocidad API vs UI, y un trazador de capas KATA. Que la sala TOQUE cada widget.

El salto de hoy

Bajas un piso: del navegador a la API 🪜

✅ Lo que ya sabes

Escribir un test() · Page Objects · fixtures con use(). Probar la app por la pantalla.

🎯 Lo que sumas hoy

Probar la app por debajo de la pantalla: golpear el endpoint HTTP directo. Más rápido, más estable, más barato.

// La pirámide del testing

      /\        E2E (UI)   lento, frágil
     /  \       pocos
    /----\      API        ⟵ hoy: rápido
   /      \     muchos          y estable
  /--------\    Unit       los devs
🧭

La capa API es el punto dulce de QA: cubre lógica de negocio real, sin el costo ni la fragilidad de manejar un navegador.

Encuadre: la pirámide de testing. Arriba E2E (UI) — realista pero lento y frágil. Abajo unit (de los devs). En medio, la capa API: el punto dulce de QA. Un test de API arranca en milisegundos (no abre navegador), no se rompe porque cambió un color o un botón, y prueba la lógica de negocio de verdad: validaciones, permisos, estados. Mensaje clave de la sesión: si un caso se puede probar por API, pruébalo por API — guarda el navegador para lo que SOLO se ve en pantalla.

⭐ QUIZ relámpago · calentamiento

Un test de API es, en una frase…

💡 Un test de API habla con el backend en su idioma: GET, POST, PUT, DELETE sobre una URL, y comprueba lo que vuelve. Sin pantalla, sin clics — solo petición y respuesta. Hoy aprendemos a escribirlos con Playwright.

Calentamiento que fija el vocabulario: petición → respuesta. La opción A confunde API con E2E (el error más común al empezar). La C lo confunde con análisis estático. Si la sala duda, recálcales: API test = mandar una request HTTP y verificar la response. Es exactamente lo que hace Postman, pero automatizado y dentro de la suite.

requestasercionesschema · datoskata

El dolor: abrir un navegador entero para probar un endpoint 🐌

orders.spec.ts — probando la API… por la UI
test("el pedido se crea", async ({ page }) => {
  await page.goto("/login");              
  await page.fill("#email", EMAIL);       │ 8 segundos de
  await page.fill("#password", PASS);     │ navegador, clics
  await page.click("#login");             │ y esperas…
  await page.goto("/orders/new");         
  await page.fill("#item", "Coffee");     
  await page.click("#submit");            
  await expect(page.locator(".toast")).toBeVisible();  // ¿de verdad se guardó?
});
🚨

Solo quería saber si POST /orders funciona. Pagué 8 segundos, un login completo y un navegador… y al final confío en un toast, no en la respuesta real del servidor.

Deja que ELLOS sientan el desperdicio: para verificar UN endpoint, montamos login + navegación + formulario + un navegador entero. Y peor: la aserción final mira un toast en pantalla, no la respuesta HTTP. Si el toast aparece pero el backend devolvió 500, el test pasa en falso. El gancho del acto: hay una puerta directa al backend que evita TODO esto. Sostén la incomodidad antes de revelar la fixture request.

requestasercionesschema · datoskata

La cura: la fixture request — HTTP sin navegador 🚀

test("POST /orders", async ({ request }) => {
  //                          ▲
  //         otra fixture, como page
  const res = await request.post("/orders", {
    data: { item: "Coffee", qty: 2 },
  });

  await expect(res).toBeOK();   // 2xx
});

Sin goto, sin clics, sin esperar render. Una petición directa, una respuesta directa. Milisegundos, no segundos.

🎁 request es una fixture built-in

Igual que page, Playwright te la presta lista. Es un cliente HTTP: APIRequestContext.

📨 Habla HTTP directo

.get() · .post() · .put() · .delete(). El mismo idioma del backend.

🧬 Cero conceptos nuevos

Pides { request } en vez de { page }. Mismo await, mismo expect. Solo cambia el canal.

La revelación del acto: existe una fixture hermana de page llamada request. Es un APIRequestContext — un cliente HTTP completo que Playwright arma y limpia por ti, con el baseURL y los headers de tu config ya aplicados. Mensaje tranquilizador: no es una librería nueva ni conceptos nuevos; pides { request } en lugar de { page } y ya. El cambio mental es: dejas de manejar la pantalla y empiezas a hablar con el servidor en su idioma (verbos HTTP). El toBeOK lo vemos a fondo en el acto 2.

requestasercionesschema · datoskata

Anatomía: una petición, una respuesta 📦

🚩 Los 4 verbos que usarás siempre

// leer
await request.get("/orders");
await request.get("/orders/42");

// crear (con cuerpo JSON)
await request.post("/orders", { data });

// actualizar / borrar
await request.put("/orders/42", { data });
await request.delete("/orders/42");

📥 Qué te devuelve cada llamada

const res = await request.get("/orders/42");

res.status();    // 200, 404, 500…
res.ok();        // true si es 2xx
await res.json();  // el body como objeto JS
res.headers();   // los headers
🔑

status() y ok() son síncronos. json() es async — el body llega como stream, por eso lleva await.

El mapa mínimo y verídico de Playwright. Izquierda: los 4 verbos. El segundo argumento { data } serializa a JSON automáticamente y pone el header content-type. Derecha: la respuesta (APIResponse). Detalle que confunde a todos: status() y ok() son síncronos (ya tienes el status apenas vuelve), pero json() es async porque el cuerpo se lee como stream — de ahí el await res.json(). En la siguiente slide lo ejecutan en vivo contra una API falsa que responde de verdad.

🎮 Playground · código real, servidor real (en memoria)

Tu cliente de API, hablando con el backend 🤖

🧪 Test runner

tu test envía peticiones HTTP →

HTTP
🌐 bunkai-bank API · /orders
POST /orders → 201
GET /orders/:id → 200 · 404
PUT /orders/:id → 200
DEL /orders/:id → 204
🗄️ base de datos (en memoria)
— sin registros —
REQ
orders-api.spec.ts — clase real, ejecución real
▶ Ejecuta — tu CLIENTE va a enviar peticiones reales…

Este runner EJECUTA JavaScript real: la clase OrdersApi corre tal cual contra una API falsa en memoria que valida, escribe en una "base de datos" visible y devuelve status reales. Tres demos en vivo: (1) ejecutar tal cual → POST 201, GET 200, PASS verde, y la fila aparece en la DB; (2) cambiar expect(body.item).toBe("Coffee") por "Tea" → FAIL con expected/received legible; (3) el experimento estrella: dentro de createOrder, cambiar "/orders" por "/order" (typo) → POST /order → 404 → falla → corregir EN UN SOLO LUGAR → verde. Ese momento ES el argumento del componente de API: el endpoint vive centralizado, igual que un selector en un Page Object.

⭐ QUIZ 1 · La fixture request

Quieres probar GET /orders sin tocar la interfaz. ¿Qué fixture pides en tu test?

💡 { request } es el cliente HTTP: golpea el endpoint sin abrir navegador. Pedir { page } o { browser } arranca un navegador que no necesitas — segundos perdidos en cada test. Para probar la API, habla con la API.

Si hicieron el experimento del endpoint en el runner, este quiz formaliza lo vivido. Opción A es el reflejo de quien aún piensa en UI; B es la respuesta. Transición: "request resuelve el canal… pero toBeOK() solo mira el status. ¿Y si el status es 200 pero el body viene mal? Ese es el siguiente dolor: aserciones de verdad."

requestasercionesschema · datoskata

Nuevo dolor: "200 OK" no significa "correcto" ⚠️

test("trae el pedido", async ({ request }) => {
  const res = await request.get("/orders/42");

  await expect(res).toBeOK();   // ✅ 200… ¿y ya?
});

// El server respondió 200, pero el body era:
// { "id": 42, "item": null, "qty": -3 }
// ☠️ datos basura, y el test pasó feliz
🕳️

Verificar solo el status es un colador: el endpoint puede responder 200 con un body incompleto, campos null o tipos equivocados.

🎯 Tres capas que verificar

1. Status — ¿el código correcto? 2. Body — ¿los datos correctos? 3. Contrato — ¿la forma correcta?

🧩 El síntoma

Tests "verdes" que no atrapan nada. Pasan aunque el backend devuelva basura, porque solo miran el status.

💡 La idea

Afirmar sobre el contenido: campos exactos, tipos, y la forma completa del JSON (schema).

El dolor del acto: una aserción débil da falsa seguridad. 200 OK solo dice "el servidor respondió", no "respondió bien". Tres niveles a cubrir: status (¿201 o 422?), body (¿item es "Coffee" y qty es 2?), y contrato/schema (¿el JSON tiene todos los campos con los tipos correctos?). El ejemplo del body con null y qty negativo es el clásico: pasa el status, pasa el test, y el bug llega a producción. La cura viene en las dos slides siguientes.

requestasercionesschema · datoskata

La cura: afirma status, body y headers 🎯

test("GET /orders/42 completo", async ({ request }) => {
  const res = await request.get("/orders/42");

  // 1 · STATUS
  expect(res.status()).toBe(200);

  // 2 · BODY (el contenido importa)
  const body = await res.json();
  expect(body.id).toBe(42);
  expect(body.item).toBe("Coffee");
  expect(body.qty).toBeGreaterThan(0);

  // 3 · HEADERS
  expect(res.headers()["content-type"])
    .toContain("application/json");
});

🔢 Status exacto

toBe(201) para creación, 422 para validación, 404 para no-existe. El código cuenta una historia.

📋 Body campo a campo

toBe · toEqual · toBeGreaterThan · toBeDefined. Las mismas aserciones que ya usas.

📨 Headers cuando importan

content-type, paginación, rate-limit, auth. No siempre, pero cuando el contrato lo exige.

La cura por capas, con matchers que YA conocen (toBe, toEqual, toBeGreaterThan) — no hay API nueva de aserciones, es el mismo expect de siempre aplicado al JSON. Mensaje sobre el status: cada código cuenta una historia y debes afirmar el EXACTO, no solo "2xx" — 201 vs 200 distingue "creé" de "ya existía", 422 vs 400 distingue validación de error genérico. El body es donde se atrapan los bugs reales. Headers: solo cuando el contrato lo pide. Pero afirmar 8 campos a mano es tedioso y frágil — eso abre la siguiente slide: validar la forma completa con schema.

requestasercionesschema · datoskata

El nivel pro: validar la forma completa con un schema 📐

En vez de afirmar 10 campos a mano, defines la forma esperada una vez (con Zod) y validas todo el body de golpe. Si falta un campo o cambia un tipo, el schema lo caza.

import { z } from "zod";

const OrderSchema = z.object({
  id:   z.number(),
  item: z.string(),
  qty:  z.number().positive(),
});

const body = await res.json();
OrderSchema.parse(body);  // 💥 lanza si la forma no calza
🔗

En este repo los schemas se generan desde la OpenAPI (bun run api:syncapi/schemas/). El contrato del backend y tus tests no pueden divergir.

schema-check.js — valida el body en vivo
▶ Ejecuta para validar el body…

El salto de "afirmar campos sueltos" a "validar el contrato completo". Zod define la forma una vez y parse() lanza si algo no calza — un campo faltante, un tipo cambiado, un null inesperado. Dato verídico y potente del repo: los schemas NO se escriben a mano, se generan desde la OpenAPI del backend con bun run api:sync hacia api/schemas/. Eso significa que si el backend cambia su contrato, tus tipos se actualizan y los tests que asumían lo viejo fallan en compilación — trazabilidad real entre contrato y prueba. Demo del live cell: ejecutar tal cual → OK; cambiar qty a -3 o item a 99 → ver los errores concretos. Que la sala rompa el body y vea el validador atraparlo.

🧠 Visualizador · el viaje de una petición

Qué pasa en await request.post(...), paso a paso

test("POST /orders", async ({ request }) => {
const res = await request.post("/orders", {
data: { item: "Coffee", qty: 2 },
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body.id).toBeDefined();
});
paso 0 / 6

📍 ¿Dónde está la petición?

— en reposo —

Presiona "Siguiente paso": vas a seguir la petición desde tu test hasta el servidor y de vuelta.
consola…

Vuelve tangible el viaje de una request HTTP. Paso 1: se arma la petición (verbo POST + ruta + body JSON). Paso 2: await la envía por la red y el control se PAUSA esperando. Paso 3: el servidor la recibe, valida, escribe en DB y prepara la respuesta 201. Paso 4: la respuesta vuelve y await se resuelve con el objeto res. Paso 5: afirmamos el status (síncrono, ya está). Paso 6: await res.json() lee el body (stream) y afirmamos un campo. Mensaje a martillar: el await del paso 2 es la espera de RED; el await del paso 6 es la espera de leer el CUERPO. Dos awaits, dos esperas distintas — eso confunde a casi todos al inicio.

⭐ QUIZ 2 · Aserciones

Tu test hace expect(res).toBeOK() y pasa. Pero el body llegó { item: null, qty: -3 }. ¿Qué falló en tu test?

💡 toBeOK() solo mira el status. Un 200 con datos basura pasa igual. Una buena aserción de API cubre las tres capas: status exacto, body (campos + tipos, idealmente con schema) y headers cuando el contrato lo pide. Ninguna herramienta adivina qué body es "correcto" — eso lo defines tú.

Caza la falsa seguridad del status-only. Opción A es el error mental central del acto; C es el "que la herramienta piense por mí". La respuesta B reactiva las tres capas. Si aciertan masivo, internalizaron que probar API = verificar contenido, no solo conexión. Transición: "Ya sabes enviar y verificar. Pero cada test necesita un usuario logueado y datos previos. Volver a hacerlo por UN endpoint a mano es el siguiente dolor: el setup de datos."

requestasercionesschema · datoskata

Nuevo dolor: cada test re-crea su mundo 🔁

test("actualiza un pedido", async ({ request }) => {
  // 1 · login para sacar token…
  const auth = await request.post("/login", {data:creds});
  const { token } = await auth.json();

  // 2 · crear un pedido para tener qué editar…
  const made = await request.post("/orders", {
    headers: { Authorization: `Bearer ${token}` },
    data: { item: "Coffee", qty: 1 },
  });

  // 3 · …y recién AQUÍ empieza el test real 😩
});

🧩 El síntoma

Login + crear precondiciones antes de cada test. 15 líneas de ritual por 3 líneas de prueba real.

🔑 El token regado

El Authorization: Bearer repetido en cada llamada. Cópialo mal una vez y son 401 misteriosos.

💡 La idea

Una fixture que loguee una vez, entregue un cliente ya autenticado y datos frescos — y limpie al final. Lo que viste con use(), ahora para API.

El dolor conecta directo con la sesión de fixtures: el setup de API también se repite. Dos caras: (1) la autenticación — sacar el token y arrastrarlo en cada header; (2) las precondiciones — crear el pedido que vas a editar/borrar. La idea es la misma fixture con use() del patrón anterior, ahora preparando un request ya autenticado + datos. Subraya el riesgo del token a mano: un Bearer mal copiado da 401 que parecen bugs del backend pero son del test. La solución: centralizar auth y data en fixtures componibles — exactamente lo que KATA hace en el acto 4.

requestasercionesschema · datoskata

La cura: auth una vez + datos únicos por test 🔐

🔑 Cliente autenticado, listo

export const test = base.extend({
  api: async ({ playwright }, use) => {
    // login UNA vez, reusa el token
    const ctx = await playwright.request.newContext({
      baseURL: BASE,
      extraHTTPHeaders: { Authorization: `Bearer ${TOKEN}` },
    });
    await use(ctx);     // el test lo recibe ya logueado
    await ctx.dispose();
  },
});

Todas las llamadas salen ya con el token. Cero Bearer regados.

🎲 Datos frescos = tests independientes

import { faker } from "@faker-js/faker";

// cada test genera SU propio dato único
const order = {
  item: faker.commerce.productName(),
  qty:  faker.number.int({ min: 1, max: 9 }),
};
await api.post("/orders", { data: order });
🧱

Datos únicos = ningún test pisa a otro. Es el requisito para poder correr en paralelo sin choques.

Las dos piezas que ordenan el setup de API. Izquierda: una fixture api que crea un APIRequestContext con baseURL y el header Authorization ya puestos (newContext + extraHTTPHeaders) — el token se resuelve una vez y todas las llamadas salen autenticadas. dispose() limpia al final. Derecha: faker para datos únicos por test. Esto es CRÍTICO y conecta con paralelización: si dos tests crean "Coffee" con el mismo id fijo, chocan; con faker cada uno tiene su propio dato y son verdaderamente independientes. La independencia es el boleto de entrada al paralelismo, que medimos en la siguiente slide.

🎮 Simulador · misma cobertura, distinta capa

6 casos: por UI vs por API — mira el reloj ⏱️

capa de test:
Reloj de pared
Cobertura6 / 6 casos
Velocidad vs UI

El widget estrella del acto. Los MISMOS 6 casos de negocio, ejecutados en tres capas. Demo: arranca en "API puro" → reloj ~5s. Cambia a "UI · E2E" → ~29s. Cambia a "Híbrido" → ~11.5s. Dos verdades que la sala debe ver: (1) la COBERTURA es idéntica — 6/6 casos en las tres; lo que cambia brutalmente es el reloj. (2) API es ~5-6× más rápido que UI para la misma verificación, porque no hay navegador, render ni esperas visuales. El híbrido (setup por API + una verificación por UI) es el punto medio realista. Mensaje: elige la capa más barata que aún pruebe lo que necesitas. Reserva la UI para lo que SOLO se ve en pantalla.

⭐ QUIZ 3 · Datos y velocidad

¿Por qué cada test de API debe generar sus datos con faker en vez de usar un id fijo como "order-1"?

💡 Datos fijos compartidos = colisiones: dos tests crean "order-1" y uno rompe al otro. Datos únicos por test = aislamiento total, y el aislamiento es lo que permite paralelizar sin flakiness. La velocidad de API solo se aprovecha si los tests no se estorban entre sí.

Une datos (faker) con velocidad (paralelismo). La respuesta exige entender que la independencia es la condición para correr rápido. Opción A subestima (es un beneficio menor); C inventa una obligación. Si en el simulador vieron el salto de velocidad, aquí entienden el requisito que lo habilita: sin independencia, paralelizar multiplica flakiness, no velocidad. Transición al cierre: "request, aserciones, datos, velocidad… ¿cómo se organiza todo esto a escala en el repo real? KATA, ahora por el lado de la API."

requestasercionesschema · datoskata

KATA: todo lo de hoy, ordenado en capas 🥋

KATA (Component Action Test Architecture) no inventa nada: organiza el cliente, las aserciones y los datos en 4 capas, donde cada una extends la anterior. El lado API es idéntico al UI — solo cambia la base.

🎁 Cliente → Componentes

Tu OrdersApi es un componente de dominio. Ahí viven las ATCs de API.

🎯 Aserciones → dentro de la ATC

Status y schema fijos van dentro; las de negocio, en el test.

🔧 Auth + datos → Fixtures (L4)

{ api } entrega el cliente autenticado y listo.

// La cadena de herencia REAL del repo (API)

class TestContext { ... }        // L1 · config, faker
class ApiBase extends TestContext // L2 · helpers HTTP
class OrdersApi extends ApiBase // L3 ⟵ tu cliente

// y la fixture lo inyecta:
class ApiFixture {
  orders = new OrdersApi(options);
}
🧬

Misma arquitectura que el lado UI: solo cambia ApiBase por UiBase. Aprendes una vez, aplicas a ambos.

Tesis del acto: KATA es la síntesis de request + aserciones + datos en una estructura. Mapea explícito: tu cliente OrdersApi = componente (L3), las aserciones fijas viven dentro de la ATC, la auth y los datos los inyecta la fixture { api } (L4). La cadena TestContext → ApiBase → OrdersApi es real y verídica (kata-architecture.md), y es ESPEJO de la cadena UI (TestContext → UiBase → LoginPage). Mensaje que baja el miedo: el lado API y el lado UI comparten L1 (TestContext) y solo difieren en L2 (ApiBase vs UiBase). Si entendiste un lado, entiendes el otro.

requestasercionesschema · datoskata

Las 4 capas de KATA, lado API 🏛️

L4 · Fixtures (DI)ApiFixture · TestFixture — inyectan el cliente autenticado
↑ usa
L3.5 · Steps cadenas de ATCs reutilizables (precondiciones)
↑ usa
⭐ L3 · Componentes de dominio OrdersApi · UsersApi — aquí viven las ATCs
↑ extends
L2 · ApiBase helpers HTTP: get/post/put/del + parseo + auth
↑ extends
L1 · TestContext config · logger · faker · entorno (compartido con UI)
📐

Regla única: una capa superior puede usar una inferior, nunca al revés. Tu cliente de API (L3, estrella) es el corazón — escribes ATCs ahí y todo lo demás ya viene hecho.

El mapa de KATA en su versión API, verídico (kata-architecture.md §1). De abajo arriba: TestContext (utilidades globales, compartido con UI), ApiBase (helpers HTTP: envuelve request, parsea respuestas, aplica auth), Componentes/ATCs (la estrella, tu OrdersApi), Steps (cadenas reutilizables para precondiciones, opcional), Fixtures (DI, entregan el cliente listo). La regla de direccionalidad mantiene el orden. Marca la capa estrella: ahí pasa el equipo el 90% del tiempo escribiendo ATCs. ApiBase y las fixtures ya vienen en el boilerplate — no se reescriben.

requestasercionesschema · datoskata

La pieza nueva: la ATC de API 🎯

class OrdersApi extends ApiBase {

  @atc("PROJ-201")
  async createOrder(data) {
    const res = await this.post("/orders", { data });

    // aserciones FIJAS dentro:
    expect(res.status()).toBe(201);
    const body = await res.json();
    OrderSchema.parse(body);   // contrato
    return body;
  }
}

🎯 ATC = un caso completo

Acceptance Test Case: no una llamada suelta, sino un mini-flujo — request → status fijo → schema → devuelve datos.

@atc("PROJ-201")

El decorador ata la ATC a su ticket de Jira 1:1. Trazabilidad y tracing automático.

⚖️ Fijas dentro, negocio fuera

Status 201 y schema (siempre ciertos) van dentro. Las aserciones de negocio van en el test.

La única pieza realmente nueva: la ATC de API y el decorador @atc. Código real, espejo del LoginPage del repo. Define ATC: caso completo (request + aserciones fijas + devuelve datos para que el test siga), no una llamada aislada. Las aserciones FIJAS (status 201 esperado, schema del contrato) viven dentro porque son parte de "la acción terminó bien"; las de NEGOCIO (¿el total es el correcto? ¿aparece en la lista del usuario?) van en el test. Es la misma regla acciones-vs-decisión del lado UI. Reglas duras: ATC atómica (nunca llama a otra ATC; las cadenas son Steps), y el @atc da trazabilidad 1:1 con Jira.

🧠 Trazador · sigue una llamada por las 4 capas

Un test llama api.orders.createOrder() — ¿por dónde viaja?

test("PROJ-201: crea pedido", async ({ api }) => {
await api.orders.createOrder(data);
});
// L4 ApiFixture.orders = new OrdersApi(opts)
// L3 class OrdersApi extends ApiBase
// L2 class ApiBase extends TestContext
// L1 class TestContext { config, faker, request }
paso 0 / 5

🏛️ Capa activa

— en reposo —

Presiona "Siguiente paso": vas a seguir la llamada bajando por las 4 capas de KATA.

Cierra el círculo: una sola línea de test (api.orders.createOrder) atraviesa las 4 capas. Paso 1: el test pide la fixture { api } (L4). Paso 2: la fixture ya tiene OrdersApi instanciada y autenticada como api.orders (L4→L3). Paso 3: createOrder es la ATC en OrdersApi (L3). Paso 4: usa el helper this.post() heredado de ApiBase (L2). Paso 5: que a su vez hereda config/faker/request de TestContext (L1). Mensaje: el test escribe UNA línea legible; las 4 capas hacen el trabajo pesado debajo. La arquitectura pagando dividendos.

requestasercionesschema · datoskata

En la práctica: pides la fixture según lo que pruebas 🎚️

Tipo de testFixture¿Navegador?Cuándo
API pura (integration){ api }NoTesting de API. Default en tests/integration/
Solo UI{ ui }Flujo de UI sin setup por API
Híbrido{ test }Setup por API + acción/verificación UI
Precondiciones repetidas{ steps }Depende3+ ATCs repetidas en 3+ archivos
// API pura — NO abre navegador (lazy)
test("PROJ-7: lista pedidos", async ({ api }) => {
  await api.orders.getOrders({ limit: 10 });
});
// Híbrido — crea por API, verifica por UI
test("PROJ-9: aparece en lista", async ({ test }) => {
  const o = await test.api.orders.createOrder(data);
  await test.ui.orders.verifyVisible(o.id);
});

Cierre operativo y verídico (kata-architecture.md §7): la tabla de selección de fixture es decisión diaria. Para API pura → { api }: como las fixtures son lazy, NO abre navegador → suites de integración en milisegundos. El { test } híbrido es la joya: crea el estado por API (rápido) y verifica por UI (realista), compartiendo contexto — el patrón que el simulador de la slide 16 llamó "híbrido". Estos snippets son el patrón real del repo. Con esto, el alumno sabe LEER cualquier test y ELEGIR su fixture con criterio de costo.

⭐ QUIZ 4 · KATA

En una ATC createOrder(), ¿qué aserción va dentro de la ATC y cuál en el test?

💡 Aserciones fijas e inevitables (un 201 al crear, el schema del contrato) son parte de "la acción terminó bien" → van dentro. Las de negocio, que cambian por caso (el total, el descuento, quién puede verlo) → van en el test, visibles. Misma regla que en el lado UI: efectos dentro, veredicto fuera.

Integra la regla acciones-vs-decisión (acto 2 de la sesión de patrones) con la ATC de API. La respuesta A separa bien: fijas dentro (201, schema), negocio fuera (montos, permisos). B mete todo dentro (oculta el veredicto); C deja la ATC sin garantía de éxito. Si aciertan masivo, dominan la frontera más sutil de KATA. Cierre del acto: pasamos al integrador y al score.

⭐ QUIZ 5 · El integrador

Conecta cada dolor con su cura:

💡 Cada herramienta cura un dolor: request habla HTTP sin navegador, aserciones + schema verifican contenido y contrato, fixtures + faker ordenan auth y datos. Y KATA apila todo en capas para que escale. Cuatro herramientas, cuatro problemas.

El quiz que amarra el arco: dolor→cura, ahora como evaluación. Respuesta correcta = el alumno internalizó que cada técnica responde a un dolor concreto, no es ceremonia. KATA cierra como el meta-marco que las organiza. Tras este quiz, celebra y pasa al score final.

Del concepto al repositorio

Dónde vive cada pieza en este repo 🗂️

tests/
  components/
    TestContext.ts        // L1
    api/ApiBase.ts        // L2 · helpers HTTP
    api/OrdersApi.ts      // L3 · cliente + ATCs
    steps/*.ts            // L3.5 · Steps
    ApiFixture.ts         // L4 · Fixtures
    TestFixture.ts        // L4 · híbrido
  integration/**          // tests API puros
  e2e/**                  // tests UI (+API)
api/schemas/              // Zod desde OpenAPI
playwright.config.ts      // baseURL, workers

📍 Tu zona de trabajo

El 90% del tiempo: escribir ATCs en components/api/ y orquestarlas en integration/.

🛠️ Ya hecho por ti

TestContext, ApiBase y las Fixtures vienen en el boilerplate. Heredas helpers; no los reescribes. Los schemas se generan: bun run api:sync.

📖 Para profundizar

Skill /test-automation + references/kata-architecture.md: reglas de ATC, fixtures y schemas.

Aterriza todo en la estructura REAL del repo. Mensaje tranquilizador: las capas bajas (TestContext, ApiBase, fixtures) ya están construidas; el alumno trabaja sobre todo en components/api (ATCs) y integration (tests). Punteros reales: la skill /test-automation y references/kata-architecture.md tienen las reglas completas de ATC y la tabla de fixtures; los schemas Zod se generan desde la OpenAPI con bun run api:sync (no se escriben a mano). Esta slide es el puente del aprendizaje a la práctica del lunes.

Cuatro herramientas, un mapa 🗺️

HerramientaDolor que curaPieza claveEn KATA
🚀 request fixturenavegador para un endpointrequest.post(...)ApiBase (L2)
🎯 Aserciones + schema"200" no significa correctostatus + body + Zoddentro de la ATC (L3)
🔧 Fixtures + fakersetup/auth/datos repetidos{ api } + datos únicosFixtures (L4)
🥋 KATAtodo junto, a escala4 capas + ATCel marco completo
🏆

Cruzaste de "probar por la pantalla" a probar el backend directo: más rápido, más estable, más profundo. Lo que hace un SDET de API.

El recap de una mirada: tabla que cruza herramienta × dolor × pieza × lugar en KATA. Pídeles que la fotografíen o la peguen en Confluence — es el resumen portátil. Nombra el logro: pasaron de probar por la UI a probar el backend directo, que es el salto a SDET de API. Prepara el score final.

Misión cumplida

Tu puntaje final

— / 6

🏠

Re-juega en casa

Este archivo corre offline: vuelve al runner de API, al validador de schema y al simulador de velocidad. Toca todo.

🧪

Reto puente

Abre tests/components/api/ del repo y ubica: capa, ATC, decorador @atc, aserciones fijas, schema.

🥷

Próximo paso

Escribe tu primera ATC de API real con /test-automation — Plan → Code → Review sobre KATA.

Hoy: request → aserciones + schema → fixtures + faker → KATA. De la pantalla al backend.

Celebra el score y nombra el logro: hoy aprendieron a probar el backend directo — request, aserciones de contenido + schema, fixtures con auth y datos, y KATA para escalar. Todo vivido en vivo, no memorizado. Reto puente sin computadora: abrir un componente api real e identificar cada concepto en código de producción. Próximo paso accionable: la skill /test-automation para escribir su primera ATC de API. Comparte el HTML — funciona offline.

QA Engineering · API Automation

De la pantalla al backend 🔌

request · aserciones · schema · KATA.
Probar la API: rápido, estable y al hueso.

¿Preguntas?/test-automationreferences/kata-architecture.md

Slide de cierre y espacio para preguntas. Reabre el runner de API o el simulador de velocidad si alguien quiere ver algo otra vez — son la mejor herramienta para resolver dudas en vivo. Punteros finales: la skill /test-automation y la referencia de arquitectura KATA. Gracias y a escribir ATCs de API.