Saltar al contenido principal

Prompts para Generar Tests E2E con Playwright e IA

10 prompts para crear tests end-to-end con Playwright usando IA: selectores robustos, Page Objects, waitFor y evitar tests frágiles.

Fran Cobos 21 min de lectura 4176 palabras

Tabla de contenidos

Tienes un suite E2E en verde y, misteriosamente, todo va bien. Luego alguien cambia un margin-bottom en un componente, un <h3> pasa a <span>, el equipo de diseño decide que el botón de comprar pasa a la derecha, y la noche te quedan 34 tests rojos. Ninguno de ellos detectó un bug real. Los 34 fallaron porque el selector dejó de encontrar lo que buscaba.

Ese es el problema crónico de los tests E2E: son caros de escribir, lentos de ejecutar y absolutamente frágiles si no se escriben con una disciplina concreta. Y la IA, por defecto, genera exactamente el tipo de test que se rompe: selectores por clase CSS, esperas fijas de dos segundos, cadenas de waitForTimeout, asserts sobre clases CSS en lugar de sobre contenido.

Lo que viene a continuación es lo contrario: diez prompts que producen tests E2E que sobreviven a un refactor de UI. No son prompts para “escríbeme un test de login”. Son prompts que obligan a la IA a entender tu aplicación, a respetar tu jerarquía de selectores y a generar assertions que describan comportamiento observable, no estructura interna.

Antes de nada: si tu backend también necesita cobertura, el articulo de tests de API REST es el complemento natural de este. Y si estás dudando entre herramientas de navegador antes de invertir en esto, la comparación está al final.

Antes de copiar: siete reglas que evitan el 80% de los tests frágiles

1. Los tests E2E verifican comportamiento, no implementación. Si tu assert comprueba que un div tiene una clase CSS, estás atando el test a una decisión de diseño. Si comprueba que el usuario ve un texto tras completar el pago, estás atando el test a lo que importa.

2. Cero esperas fijas. Ni waitForTimeout, ni sleep, ni setTimeout en el test. Playwright tiene auto-waiting: reintenta la acción y la assertion automáticamente hasta que el estado o el elemento aparezca. Si necesitas esperar, es que te falta una condition en el locator.

3. Los selectores se eligen de arriba abajo. En este orden: getByRole con nombre, getByLabel, getByText para contenido estático, getByPlaceholder como último recurso visible, y data-testid solo cuando no hay semántica. Los #id generados por el framework son la peor opción de todas.

4. Un Page Object por pantalla, un locator por elemento reutilizable. Si el mismo botón aparece en tres tests, su locator vive en un Page Object. Duplicar locators es la vía más corta a una suite imposible de refactorizar.

5. Aisla los datos, no el estado. Cada test parte de datos conocidos. Nada de “el test 2 depende de que el test 1 haya creado el usuario”. Usa factories con datos generados por test, como ves en mi tutorial de Prisma.

6. Pregunta a la IA por qué un selector es frágil, no solo cuál usar. Si le pides “elige el mejor selector” te da uno. Si le pides “elige el mejor selector y explica por qué los otros fallarían con un refactor de UI”, entiendes la decisión y puedes defenderla en el code review.

7. Nada de tests que nunca has visto fallar. Un test que no has visto rojo no es un test. Si lo rompes a propósito y sigue verde, no estaba probando nada. Pide ese exercise a la IA explícitamente.

1. Analizar la aplicación antes de generar nada

Antes de escribir tests E2E, necesito que analices mi aplicación y
construyas el mapa de flujos críticos.

Stack: Next.js [App Router | Pages Router] + [Tailwind | CSS Modules]
Componentes relevantes:
[pegar las rutas de app/ o pages/ y los componentes principales]
Formularios de auth en: [ruta]
Formulario de [proceso principal] en: [ruta]
Estados vacíos, de carga y de error en: [lista de rutas]

Quiero tres salidas:

1. INVENTARIO DE FLUJOS: para cada flujo crítico (registro, login,checkout,
   edición de perfil, recuperación de contraseña), la secuencia de acciones
   del usuario y los estados intermedios visibles.

2. SELECTORES RECOMENDADOS por elemento clave, con una tabla: elemento,
   selector elegido (getByRole / getByLabel / data-testid), y una nota de
   por qué los otros métodos fallarían ante un cambio de UI.

3. QUÉ DEBE MODIFICARSE EN EL CÓDIGO DE APLICACIÓN para que los tests sean
   estables. Concretamente: qué roles ARIA faltan, qué etiquetas de
   formulario no están asociadas, y qué data-testid habría que añadir.
   Dame el diff exacto.

El punto 3 es obligatorio. Prefiero cambiar el componente para que sea
testeable antes que meter un selector frágil en el test.

Por qué funciona: este prompt invierte el orden habitual. En lugar de generar tests y descubrir que son frágiles, mapeas la aplicación y decides qué debe cambiar para que los tests sean sólidos. El punto 3 es donde está el valor real: te devuelve un diff de accesibilidad que mejora el producto además de los tests.

Ejemplo real de salida del punto 3:

// ❌ Antes: el botón no tiene nombre accesible
<button className="btn-primary" onClick={submit}>
  <ArrowRightIcon />
</button>

// ✅ Después: nombre accesible + selector por rol estable
<button
  type="submit"
  aria-label="Continuar al pago"
  className="btn-primary"
  onClick={submit}
>
  <ArrowRightIcon aria-hidden="true" />
</button>
// El test ya no depende de la clase, ni del icono, ni del texto
await page.getByRole('button', { name: 'Continuar al pago' }).click();

2. Generar el primer test de un flujo crítico

Escribe UN test Playwright para el flujo de [nombre del flujo].

Contexto de la aplicación:
[pegar la página/ruta, el componente principal y el código del submit]

Test file: tests/e2e/[nombre].spec.ts
Runner: Playwright Test (@playwright/test, no Cypress, no Puppeteer)

Requisitos obligatorios:
- Usa `test.step()` para etiquetar cada fase: "Rellenar el formulario",
  "Enviar", "Esperar confirmación". El reporte se lee mucho mejor.
- Localizadores con el helper `getByRole` y `getByLabel` primero.
  Si un elemento no tiene semántica, propón el data-testid que hay que
  añadir en vez de inventar un selector por clase.
- Cero waitForTimeout. Para esperas de red usa
  `await page.waitForResponse(...)` o `await expect(locator).toBeVisible()`,
  nunca un sleep.
- Assertions web-first: expect(locator).toBeVisible(), toHaveText(),
  toHaveURL(). No uses expect(await page.content()).toContain(...).
- Precondición de datos mediante el helper de seed que ya tengo:
  [pegar o describir la firma de seedUser()]
- El test debe ser independiente: se puede ejecutar solo con `npx playwright
  test tests/e2e/[nombre].spec.ts` sin que fallen otros.

Al final, dame una frase explicando qué fallo real detectaría este test
si mañana alguien rompe la validación del formulario.

Por qué funciona: pedir test.step() y prohibir los sleeps explícitamente elimina de un golpe las dos cosas que más leyen mal los tests generados. La última frase sobre qué fallo detectaría es el mejor control de calidad: si la respuesta es vaga, el test es decorativo.

3. Selectores resilientes: el prompt de jerarquía

Necesito convertir estos localizadores frágiles de mi suite en
selectores que sobrevivan a un refactor de UI.

Localizadores actuales (pega de 5 a 10 de los peores):
[pegar]

Para cada uno, devuelve una tabla con:
| Localizador actual | Método frágil | Selector recomendado | Rol/etiqueta a añadir al componente |

Reglas estrictas, en este orden de preferencia:
1. page.getByRole('button' | 'link' | 'textbox' | 'heading' | 'combobox', { name: ... })
2. page.getByLabel('...') para controles de formulario
3. page.getByRole('...', { name: /regex/i }) cuando el texto varíe
4. page.getByTestId('...') solo si el componente no expone semántica
5. NUNCA: #id autogenerado, .clase-css, xpath, page.locator('div:nth-child(3)')

Considera además:
- Los SVG e iconos dentro de botones: necesitan aria-hidden o
  aria-label para no contaminar el nombre accesible del botón.
- Los placeholders que desaparecen al escribir: no son selectores estables.
- Los textos que dependen de datos dinámicos (nombres de usuario, fechas):
  usa regex, no el valor literal.

Para cada par (elemento, selector), explica en una línea por qué
fallaría el selector original ante un cambio de estilo o de maquetación.

Por qué funciona: la IA tiene una mención ligera a “selectores robustos” que es mostrar el primer getByTestId que encuentra. Darle la jerarquía exacta en el prompt es lo que convierte esa mención en una decisión defendible. Y pedir el ARIA que falta hace que cada test generado mejore la aplicación.

4. Page Object Model sin sobre-ingeniería

Quiero introducir Page Objects en mi suite Playwright sin crear una
capa de abstracción que me complique la vida.

Pantallas de la aplicación:
[describir: LoginPage, ProductPage, CheckoutPage, DashboardPage]

Genera:
- Un Page Object por pantalla, en tests/pages/[nombre].page.ts
- Un BasePage con helpers compartidos: navegación con espera de
  carga real, waitForToast, y un wrapper de screenshot on-failure
- Locators como propiedades readonly privadas en lugar de strings sueltos

Restricciones importantes, para no sobre-abstract:
- Un Page Object no debe contener ninguna aserción. Las assertions
  van en el test. Si metes asserts en el POM, nadie puede reutilizarlo
  para un caso negativo.
- Nada de una capa genérica de "BaseComponent" con métodos abstractos.
  Duplicar tres líneas en dos POMs es mejor que un framework propio.
- Ningún POM debe hacer más de una navegación o una acción de alto nivel
  por método. Si un método hace login + rellena + confirma, es un
  Page Object de un flujo, no de una pantalla, y hay que decidir si
  sirve.
- Nada de setters que acepten strings para todo. Los locators van fijos.

Ejemplo de API que quiero para el POM de LoginPage:

```ts
export class LoginPage {
  constructor(private page: Page) {}
  async goto() { await this.page.goto('/login'); }
  async login(email: string, password: string) {
    await this.page.getByLabel('Email').fill(email);
    await this.page.getByLabel('Contraseña').fill(password);
    await this.page.getByRole('button', { name: 'Iniciar sesión' }).click();
  }
  get errorMessage() { return this.page.getByRole('alert'); }
}

Escribe los POM siguiendo exactamente este estilo.


**Por qué funciona**: la restricción "ningún POM contiene aserciones" y "nada de capa genérica abstracta" ataca los dos extremos en los que suelen acabar las suites generados. Sin esas dos frases, la IA produce un framework propio de 400 líneas que nadie mantiene.

### 5. Estados de carga, vacío y error

Mi aplicación tiene estados asíncronos que nunca testeo y son donde nacen los bugs reales.

Componente de lista: [pegar el componente con su useEffect/useQuery/SWR]

Genera tests para cada estado, no solo para el estado con datos:

  1. Estado de carga: el spinner/skeleton aparece. Usa la assertion de que el skeleton está visible Y el contenido todavía NO, para probar que no hay flash de contenido vacío.
  2. Estado vacío: la respuesta viene con lista vacía. Assert del mensaje de “no hay nada” y del botón de acción.
  3. Estado de error: la API devuelve 500. Assert del mensaje de error user-friendly y del botón “Reintentar”. Si el componente NO tiene estado de error, dímelo explícitamente: es un bug de UX.
  4. Estado de éxito: contenido renderizado.
  5. Reintento: tras el error, hago clic en Reintentar y la API responde bien. Assert de que el error desaparece y el contenido aparece.

Para los estados 2, 3 y 5 usa MSW o el route interception de Playwright (page.route) para interceptar la llamada a [endpoint] y devolver el body que te indique. playwright route es más limpio que MSW aquí.

En el estado de carga no uses waitForTimeout para “esperar a que aparezca el spinner”: intercepta la ruta con un delay controlado y assert sobre esa demora explícita.


**Por qué funciona**: los estados no felices son los que más se olvidan y los que más se rompen en producción. `page.route()` es la herramienta ideal porque te da control fino del delay y del body, y vive dentro del propio test E2E sin dependencias extra.

### 6. Tests de formularios y validación

Genera los tests del formulario de [nombre].

Componente y esquema de validación: [pegar el componente y el schema Zod]

Cobertura que necesito, siguiendo la lógica del schema y no inventando:

  1. Envío vacío: assert del mensaje de validación de cada campo requerido, según el schema real.
  2. Valor con formato inválido (email mal formado, teléfono corto, contraseña sin los requisitos que exige mi schema): assert del mensaje exacto que devuelve el schema.
  3. Valor válido: assert de que la llamada al servidor se hace con los datos correctos (interceptando la ruta) y del estado de éxito.
  4. El servidor devuelve 422 con errores de campo: el formulario debe mostrar esos errores junto a los inputs correspondientes, no en un toast genérico. Si no lo hace, es un bug: señálalo.
  5. Doble clic en el botón de enviar: solo debe dispararse UNA petición. Intercepta la ruta y cuenta las llamadas. Esto es crítico en producción: es la causa número uno de pedidos duplicados.
  6. Botón deshabilitado mientras la petición está en vuelo.

Usa page.route para interceptar [POST /endpoint] y poder tanto contar llamadas como simular respuestas lentas o con error.

Nada de esperar con timeouts: el punto 5 se resuelve con Promise.all sobre el clic y la espera de la request.


**Por qué funciona**: el test 5 es el que más bugs reales ha encontrado en mis proyectos y el que la IA nunca genera sola. Contar llamadas a la red es un assert que va directo al comportamiento, no al DOM.

### 7. Autenticación: setup limpio sin repetir login

Mi suite E2E tiene 30 tests y todos empiezan haciendo login. Eso son 30 segundosMINUMOSadbajo en cada ejecución y un slowdown que hace que nadie lance la suite entera.

Configuración actual: [pegar playwright.config.ts] Estrategia de auth actual: [pegar el beforeEach que hace login]

Propón una migración a un proyecto de Playwright con estado reutilizable:

  • Un storageState guardado una sola vez en un global setup
  • Reutilizado por defecto, con un proyecto separado para los tests que necesitan estar SIN autenticar
  • El setup debe usar una API real de login (POST /auth/login con las credenciales de un usuario de test seed), no meter cookies a mano: si la cookie se llama igual que en producción, el test es realista
  • Cómo generar ese storageState en el setup con request context de Playwright (no con fetch dentro de la página)
  • Cómo hacer que la sesión expire entre tests y forzar un re-login

Incluye el código completo del setup, del config y de un test de ejemplo, más el comando para regenerar el state cuando cambia el formulario de login.

Además: el login fallido (credenciales incorrectas) debe vivir en el proyecto sin auth, no en el que reutiliza state.


**Por qué funciona**: separar el estado autenticado como un artefacto reutilizable es la diferencia entre una suite de 4 minutos y una de 40. Y pedir que el login se haga contra la API real evita el clásico storageState fabricated que pasa el test de local y falla en CI.

### 8. Responsive y viewports

Mi aplicación es responsive y quiero cubrir al menos el breakpoint crítico. Genera tests con Playwright projects para varios viewports.

Breakpoints de mi diseño: [pegar el config de Tailwind: sm, md, lg, xl]

Genera:

  1. Un proyecto desktop (1440x900) y uno móvil (390x844, isMobile, hasTouch)
  2. Un test parametrizado con test.describe que itere sobre viewports sin duplicar la lógica del test
  3. Checks de responsive que valgan de verdad: que el menú hamburguesa aparece por debajo del breakpoint, que el contenido no desborda horizontalmente (assert de document.documentElement.scrollWidth <= innerWidth), y que los targets táctiles del móvil son >= 44px
  4. Un check de que el layout no se rompe con un texto largo: inyecta contenido con un string de 200 caracteres vía una ruta de test o un query param, y assert que no hay scroll horizontal

Nada de screenshots como aserción, salvo que me digas exactamente cómo se comparan. Prefiere asserts sobre visible/hidden y medidas. Si un test necesita validar el diseño visual, dímelo y lo haré con Playwright visual comparisons aparte.


**Por qué funciona**: el test de scroll horizontal es el que más veces encuentra bugs reales, porque un solo elemento con `min-width: 900px` en un card pasa todos los tests funcionales y arruina el móvil. Los `test.describe` parametrizados evitan duplicar la lógica entre viewports.

### 9. Debug: trazas, vídeos y logs

Tengo tests E2E que fallan de forma intermitente y no sé por qué. Necesito instrumentación para diagnosticar.

Genera la configuración de Playwright con:

  • trace: ‘on-first-retry’ en local, ‘retain-on-failure’ en CI
  • screenshot: ‘only-on-failure’
  • video: ‘retain-on-failure’ con size limitado a 1280x720
  • Un reporter html con el informe abierto (o vídeo ya visto)

Y además un helper attachDiagnostics(page, testInfo) que:

  • Captura el estado de la red fallida en el momento del fallo
  • Guarda un screenshot con timestamp en el nombre
  • Añade al reporte el valor de las variables relevantes (URL actual, cookies no sensibles, localStorage keys)
  • Respeta la privacidad: filtra cualquier clave que parezca un token

Y el flujo de debug que voy a seguir cuando algo falle:

  1. Abrir el trace con npx playwright show-trace trace.zip
  2. Reproducir solo ese test: npx playwright test —last-failed
  3. Ver el vídeo, luego el snapshot del DOM en el paso anterior al fallo
  4. Confirmar si es un problema de la app o del test

Escribe el helper completo y explica en 3 líneas cómo leer un trace.


**Por qué funciona**: sin traza, depurar un E2E es adivinar. Con traza ves el estado del DOM, la red y la consola en cada paso. El filtro de tokens no es un detalle: los traces se acaban subiendo a artefactos de CI y un token de sesión ahí es un incidente de seguridad.

### 10. Revisión de tests frágiles existentes

Revisa mi suite E2E actual y hazme una auditoría de fragilidad.

Tests a revisar: [pegar entre 3 y 5 archivos .spec.ts representativos, o describir el patrón si son muchos]

Para cada test, clasifícalo: A) Robusto: selectores semánticos, sin esperas fijas, assertions sobre contenido, datos aislados B) Frágil por selector: depende de clase CSS, id autogenerado o nth-child C) Frágil por temporización: tiene waitForTimeout, sleep o espera de un estado que no garantiza nada D) Frágil por acoplamiento: depende del orden o del estado que dejó otro test E) Redundante: dos tests que comprueban lo mismo F) Inestable en CI: pasa en local y falla en CI, por lo general por falta de espera de red, por datos compartidos o por resolución de zona horaria

Para cada caso B, C o D:

  • El diff exacto del antes y el después
  • El porqué del fallo que producirá, redactado como el mensaje de error que verás en CI

Al final, dame un recuento por categoría y los tres cambios que más reduzcan la fragilidad por número de tests afectados.

No reescribas la lógica de los tests: solo los localizadores y las esperas.


**Por qué funciona**: una revisión categórica es mucho más accionable que "tu suite es frágil". Pedir el diff exacto de cada corrección y limitarla a localizadores y esperas evita que la IA te proponga reescribir la lógica de negocio del test, que es donde suele meterse sin que le pidas.

## Anti-patrones: los cuatro que la IA genera por defecto

❌ **Esperas fijas para "dar tiempo" a la página.** El sleep más común es para ocultar un test que en realidad necesita esperar algo concreto. Y produce tests que tardan 40 segundos y aun así fallan en CI con más carga.

```ts
// ❌ Ni rápido ni fiable
test('login funciona', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#email', 'user@test.com');
  await page.click('button.login-btn');
  await page.waitForTimeout(2000);
  await expect(page.locator('.dashboard')).toBeVisible();
});

✅ Auto-waiting con la condición real. Playwright reintenta la assertion hasta que pase o agote el timeout, así que no hay sleeps y no hay falsos negativos por lentitud.

// ✅ Rápido en local y en CI
test('login funciona', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Contraseña').fill('Password123!');
  await page.getByRole('button', { name: 'Iniciar sesión' }).click();

  await expect(page).toHaveURL(/\/dashboard/);
  await expect(page.getByRole('heading', { name: 'Bienvenido' })).toBeVisible();
});

❌ AsAssertions sobre clases CSS como prueba de comportamiento. Si cambias btn--loading por is-spinning, el test falla aunque la UX sea idéntica.

// ❌ Testea CSS, no producto
expect(await page.locator('button').getAttribute('class')).toContain('btn--disabled');

✅ AsAssertions sobre lo que el usuario percibe: visibilidad, texto, URL, atributos ARIA de estado.

// ✅ Testea comportamiento
await expect(page.getByRole('button', { name: 'Enviar' })).toBeDisabled();
await expect(page.getByRole('button', { name: 'Guardado' })).toBeVisible();

❌ Un Page Object que hace login, rellena y confirma. Nadie lo puede reutilizar para probar un caso negativo, porque siempre termina en el mismo sitio.

❌ Tests que dependen del orden. “El test 5 asume que el test 3 creó un pedido”. Con --repeat-each=2 o --shuffle en CI, la suite entera se rompe sin que nadie haya tocado el código.

Playwright vs Cypress vs Puppeteer: qué usar y cuándo

CriterioPlaywrightCypressPuppeteer
NavegadoresChromium, Firefox, WebKitChromium, Firefox, WebKit, EdgeSolo Chromium (salvo forks)
Multi-pestañaNativaLimitadaNativa
IframesNativoManualManual
DownloadsNativoMuy limitadoManual
API testingrequest fixture incluidaLimitadoNo
Espera automáticaSí, con web-first assertionsSí, con reintentosNo, manual
VelocidadRápidaMediaRápida
Runner de tests propioSí, completoNo, vive en su dashboardNo, hay que montarlo
Ideal paraSuites E2E serias en CITesting dirigido al desarrolloScraping y automatización

La recomendación práctica en 2026: Playwright para E2E en serio, y no mezclar frameworks en el mismo proyecto porque duplicas la infraestructura de tests por nada.

Errores frecuentes al generar tests con IA

El test pasa solo. La causa más común: el locator nunca existió y expect(locator).toBeVisible() se evaluó contra un elemento que sí estaba, o el await estaba fuera del test. Siempre verifica que el test falla cuando rompes la app a propósito.

Timeout de 30 segundos en cada test. Casi siempre es que el selector no encuentra el elemento y Playwright agota el timeout por defecto. Mira el error: si dice “waiting for locator”, el problema es el selector, no la velocidad.

“Cannot read properties of null” en el test. Un locator devuelto a un .click() sin await. En Playwright todo es una promesa y encadenar sin await devuelve undefined silenciosamente.

El test pasa en local y falla en CI con datos distintos. La base de datos de CI no tiene el seed o tiene datos de otro test. Aísla los datos con una factory por test y dale un namespace de email único.

Los tests se solapan y se pisan. Escribe test.describe.configure({ mode: 'serial' }) para tests que comparten estado, o usa proyectos separados con storageState distinto. Parallelismo total solo con datos completamente aislados.

CORS o URLs hardcodeadas a localhost. El baseURL debe venir de process.env.BASE_URL y el test debe usar rutas relativas. Igual con las URLs de la API en las variables de entorno.

El flujo completo

  ┌─────────────────────────────┐
  │ Prompt 1 → mapear la app    │  Flujos + ARIA que falta
  └──────────────┬──────────────┘
                 │  (+ diff de accesibilidad)
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 3 → jerarquía de    │  getByRole > getByLabel > testid
  │ selectores                  │
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 2 → primer test real │  test.step + web-first asserts
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 4 → Page Objects     │  Locators centralizados
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 5/6 → estados y      │  Loading, vacío, error, validación
  │ formularios                 │
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 7 → auth con state   │  Setup sin 30 logins
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 8 → viewports        │  Móvil y sin overflow
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 9 → trazas y vídeo   │  Diagnóstico en CI
  └──────────────┬──────────────┘
                 ▼
  ┌─────────────────────────────┐
  │ Prompt 10 → auditoría       │  Clasificar y arreglar lo frágil
  └─────────────────────────────┘

Conclusión

El E2E bien hecho no es el que más cobertura tiene, es el que aguanta un refactor de UI sin caerse. Eso se consigue con tres decisiones que la IA no toma sola si no se lo exiges: selectores semánticos en lugar de estructurales, esperas derivadas de condiciones reales en lugar de tiempos, y assertions sobre comportamiento en lugar de sobre clases CSS.

Si quieres el nivel anterior en la pirámide, el artículo de tests de API REST cubre la capa que va justo debajo, y los prompts para testear componentes React cubren la parte de UI unitaria. Los tres juntos cubren la pirámide completa, y la estrategia de qué testear primero la tienes en cómo testear código generado por IA.

Para el contexto de herramientas, mi comparativa entre Cursor, Copilot y Windsurf incluye cómo se comporta cada uno generando tests dentro del editor. Y si los prompts te salen largos y quieres recorte, la técnica de caveman prompting para ahorrar tokens funciona también aquí: los bloques de código pesan tokens y se pueden comprimir sin perder la intención.

Sobre el resto del stack, hay dos guías que van muy de la mano con esto: Docker para developers para tener el entorno de test reproducible, y deploy gratis con GitHub Actions para ejecutar la suite en cada push. Si prefieres los prompts montados, mi generador interactivo tiene una sección de testing.

Fran Cobos

Fran Cobos

Desarrollador Full Stack especializado en IA aplicada, automatización y desarrollo web. Escribo sobre herramientas, tutoriales y casos reales para programadores.

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.