Tests flaky: por qué fallan a veces y cómo cortarlos
Un test flaky da resultados distintos con el mismo código. Las cinco causas: esperas fijas en vez de esperas por condición, selectores que dependen de la posición, datos compartidos entre corridas, dependencias de orden y servicios externos sin controlar. Reintentar hasta que pase esconde el problema y a veces tapa un bug real.
Un test flaky o intermitente es el que pasa unas veces y falla otras sin que nadie haya tocado el código.
El daño real no es el test: es lo que le hace al equipo. Cuando un rojo puede significar “hay un bug” o “es el de siempre”, la gente deja de mirar los rojos. Y ahí la suite entera dejó de servir, aunque siga corriendo.
Cómo saber si tienes un problema
Corre la suite completa cinco veces seguidas sin cambiar nada. Anota qué falló en cada una.
Todo test que falle una o dos veces de cinco es flaky. Si son más del 5 % de tu suite, el equipo ya perdió la confianza aunque nadie lo diga en voz alta.
Las cinco causas
1. Esperas fijas
La causa número uno, con diferencia.
await page.click('#pagar');
await page.waitForTimeout(2000); // ¿y si hoy tarda 2.3s?
await expect(page.getByText('Gracias')).toBeVisible();
Ese waitForTimeout es una apuesta sobre lo que va a tardar el servidor. Gana casi siempre y pierde
el día que hay carga, que es justo el día que importa.
await page.click('#pagar');
await expect(page.getByText('Gracias')).toBeVisible({ timeout: 15000 });
La segunda versión avanza en cuanto aparece el texto y falla solo si no aparece nunca. Más rápida y más confiable a la vez.
Caso especial: networkidle. Parece la solución correcta y no lo es. “Sin peticiones de red”
no significa “la interfaz está lista”: una aplicación con sondeo periódico nunca queda en reposo, y
una que ya terminó de cargar puede seguir pintando. Espera por lo que te importa ver, no por el
tráfico.
2. Selectores por posición
await page.locator('.producto').nth(4).click();
Ese nth(4) asume que el elemento va a seguir estando en la quinta posición. Basta con que la lista
llegue en otro orden, o que aparezca un banner promocional, para que el test toque otra cosa.
Peor todavía: a veces no falla, hace clic en el producto equivocado y el test sigue. Un fallo silencioso.
Ancla por lo que el usuario ve:
await page.getByRole('link', { name: 'Lámpara de mesa Fjord' }).click();
3. Datos compartidos
await page.getByLabel('Correo').fill('test@ejemplo.com');
Ese usuario existe hasta que alguien limpia la base, o hasta que otro test lo modifica, o hasta que dos corridas en paralelo lo tocan a la vez.
Cada test debería crear lo que necesita y no depender de nada previo:
const correo = `test-${Date.now()}@ejemplo.com`;
4. Dependencia de orden
El test B funciona porque el test A dejó una sesión abierta, o un producto en el carrito.
Se detecta fácil: corre la suite en orden aleatorio. Lo que se rompa tenía esta dependencia escondida. Cada test debe poder correr solo.
5. Servicios externos sin controlar
Una pasarela de pago de pruebas, un servicio de correo, una API de terceros. Cuando se caen —y se caen— tu suite se pone roja sin que haya un bug tuyo.
Para los tests que no están probando esa integración, intercepta la llamada y responde tú:
await page.route('**/api/pagos', (route) =>
route.fulfill({ status: 200, body: JSON.stringify({ ok: true }) }),
);
Deja las llamadas reales para uno o dos tests dedicados a esa integración.
Lo que no debes hacer
Reintentar hasta que pase.
// en playwright.config.ts
retries: 3,
Los reintentos tienen un uso legítimo: absorber la inestabilidad de la infraestructura mientras arreglas la causa. Pero usarlos como solución permanente convierte tu suite en un generador de falsos verdes: un test que necesita tres intentos está fallando el 66 % de las veces y tú estás mirando solo el intento que salió bien.
Y hay un detalle que casi nadie considera: la intermitencia puede ser un bug real de tu aplicación. Una condición de carrera que se manifiesta una de cada diez veces en el test es la misma que le va a pasar a uno de cada diez usuarios. El test no está mintiendo: está encontrando algo.
Antes de marcar un test como flaky, mira si el que falla intermitentemente es el test o el producto.
Un método para limpiar
- Mide. Corre la suite cinco veces y anota qué falló y cuántas veces. Sin datos vas a discutir opiniones.
- Ordena por frecuencia. El que falla tres de cinco antes que el que falla una de cinco.
- Clasifica la causa con las cinco de arriba. Casi siempre es la primera o la segunda.
- Arregla o saca. Un test que no puedes arreglar esta semana, sácalo de la suite principal. Que quede en cuarentena, corriendo aparte, sin bloquear despliegues ni entrenar al equipo a ignorar rojos.
- Repite cada mes. La intermitencia vuelve sola.
La relación con los falsos verdes
Los tests flaky y los falsos verdes son el mismo problema visto desde dos lados: código que continúa cuando no debería.
Un try/catch que traga un error produce un verde falso. Una espera fija demasiado corta produce un
rojo intermitente. Los dos vienen de que el test no verifica de verdad la condición que le importa.
Si estás limpiando la intermitencia, aprovecha y revisa la otra cara: cómo detectar falsos verdes en tests E2E.
Y si estás montando la suite desde cero, la guía de automatización E2E tiene el orden recomendado.
Oráculo marca los pasos que nunca alcanzaron un estado estable y guarda la duración de cada uno, que son las dos señales que delatan a un test intermitente antes de que empiece a fallar.
Sigue leyendo
- Cómo automatizar pruebas E2E: guía completaQué automatizar primero, cómo elegir herramienta, grabar o escribir, dónde ejecutar y cómo leer el resultado. La guía para montar una suite E2E que sirva.
- Playwright vs Cypress vs Selenium en 2026Comparación honesta de las tres herramientas de testing E2E: en qué gana cada una, cuánto cuestan de mantener y cuál conviene según el equipo que tengas.