Flaky tests: la guía definitiva para diagnosticarlos y erradicarlos
Un test flaky — que a veces pasa y a veces falla sin que nada cambie — es peor que no tener test: entrena al equipo a ignorar el rojo, y el día que el rojo es real, nadie lo mira. Esta guía sale de años operando suites: las seis causas raíz cubren, en nuestra experiencia, más del 95% de los casos.
Las 6 causas raíz (y su huella digital)
1. Esperas incorrectas — la campeona absoluta
Huella: falla más en CI (máquinas lentas) que local; el trace muestra un elemento que "aún no estaba".
Cura: esperas de condición, nunca de tiempo. waitForTimeout/sleep/cy.wait(ms) son deuda; espera el estado concreto que necesitas.
2. Datos compartidos entre tests
Huella: pasa solo, falla en paralelo; falla según el orden de ejecución; "funciona si corro solo ese spec". Cura: cada test crea sus datos (factories con IDs únicos) y los limpia en teardown. Prohibido depender de lo que dejó otro test.
3. Dependencia del tiempo real
Huella: falla a fin de mes, en cambio de hora, después de las 21:00 (zona horaria), o "solo los lunes".
Cura: congela el reloj (page.clock, freezegun, cy.clock). Un test que usa Date.now() sin control es una cita con el desastre.
4. Animaciones y transiciones
Huella: clicks que "no hicieron nada" o le pegaron al elemento equivocado; screenshots que difieren por un frame.
Cura: deshabilita animaciones en entorno de test (prefers-reduced-motion, CSS override) o espera su fin explícitamente.
5. Backend/entorno inestable
Huella: grupos de tests fallando juntos, con timeouts de red en el trace; staging caído a las 3am. Cura: para la suite de UI, stub de red (intercepts/routes). El entorno real se cubre con un smoke chico y monitoreado, no con toda la suite.
6. Concurrencia real de la app
Huella: la más rara y la más valiosa — el flaky reproduce una race condition que existe en producción. Cura: ninguna en el test — el bug es de la app. Antes de "estabilizar" un flaky, pregúntate siempre si te está gritando un defecto real.
El protocolo de diagnóstico (15 minutos)
- Corre el test 10 veces seguido (
--repeat-each=10en Playwright). ¿Frecuencia? - Córrelo en paralelo con la suite. ¿Solo falla acompañado? → causa 2.
- Lee el trace del fallo, no del éxito: ¿qué esperaba, qué había?
- Compara timestamps CI vs local: ¿diferencia de velocidad (causa 1) o de zona horaria (causa 3)?
La política que evita que vuelvan
- Cuarentena inmediata: el flaky sale de la suite bloqueante el mismo día que se detecta.
- Presupuesto de arreglo: una semana en cuarentena; después, se arregla o se borra. La cuarentena eterna es mentira institucionalizada.
- Retry como termómetro, no como cura: registra qué tests pasaron en retry — esa lista es tu radar de flaky en incubación.
- Cero merges con rojo, sin excepciones "porque ese siempre falla". Si siempre falla, ¿por qué sigue ahí?
Profundiza: las esperas correctas por herramienta están en los cursos gratuitos de Playwright, Cypress y Selenium.