Que Son los Tests Inestables?

Un test inestable (flaky test) es un test que pasa y falla intermitentemente sin ningun cambio de codigo. Ejecutas la suite — pasa. La ejecutas de nuevo con el mismo codigo — un test falla. La ejecutas por tercera vez — pasa. Este comportamiento no determinista destruye la confianza en la suite de tests y desperdicia enormes cantidades de tiempo investigando fallas falsas.

Google reporto que 1.5% de sus tests eran inestables, y esos tests consumian 2-16% de sus recursos de computo completos a traves de reintentos.

Causas Raiz

1. Problemas de Timing y Sincronizacion (Mas Comun)

// MAL — sleep hardcodeado
await page.click('#submit');
await page.waitForTimeout(3000);

// BIEN — esperar condicion
await page.click('#submit');
await expect(page.locator('.result')).toHaveText('Success', { timeout: 10000 });

2. Dependencias de Orden entre Tests

Tests que dependen de que otros tests se ejecuten primero.

3. Estado Mutable Compartido

Tests que modifican estado global sin aislamiento adecuado.

4. Dependencias de Servicios Externos

Tests que llaman servicios externos reales que pueden ser lentos o no estar disponibles.

5. Contention de Recursos

Tests compitiendo por recursos limitados durante ejecucion paralela.

6. Logica Dependiente del Tiempo

Tests que dependen de la hora actual o zona horaria.

Corrigiendo Tests Inestables

Asegurar Aislamiento

@BeforeEach
void isolateTest() { database.beginTransaction(); }

@AfterEach
void cleanupTest() { database.rollbackTransaction(); }

Mockear Servicios Externos

await page.route('**/api/external-service/**', route => {
    route.fulfill({ status: 200, body: JSON.stringify({ result: 'mocked' }) });
});

Sistemas de Deteccion

Modo Repeticion en Pipeline de PR

- name: Ejecutar tests nuevos 20 veces
  run: |
    for i in {1..20}; do
      npx playwright test --grep @new
    done

Dashboard de Seguimiento

Test: testCheckoutFlow
  Ultimas 100 ejecuciones: 96 pass, 4 fail (96% confiabilidad)
  Estado: FLAKY (debajo del umbral de 99%)
  Ultima falla: TimeoutError en .payment-confirmation

Sistema de Cuarentena

  1. Marcar: Agregar tag @Flaky o mover a suite de cuarentena
  2. Aislar: Remover del pipeline CI bloqueante
  3. Monitorear: Continuar ejecutando en un job separado no bloqueante
  4. Corregir: Asignar un responsable y establecer deadline
  5. Restaurar: Una vez corregido y estable por N ejecuciones, mover de vuelta
@Tag("quarantine")
@Flaky(reason = "Timeout intermitente en CI runners lentos", ticket = "BUG-123")
@Test
void testCheckoutWithCoupon() {
    // En cuarentena — se ejecuta pero no bloquea deployment
}

Mejores Practicas de Prevencion

  1. Nunca usar esperas hardcodeadas — siempre usar condiciones explicitas
  2. Ejecutar tests en orden aleatorio — detecta dependencias de orden
  3. Repetir tests nuevos — ejecutar 20-50 veces antes de mergear
  4. Mockear servicios externos — eliminar variabilidad de red
  5. Usar datos de test unicos — evitar conflictos entre tests paralelos
  6. Revisar metricas de inestabilidad semanalmente

Ejercicios

Ejercicio 1: Diagnosticar y Corregir

Toma 3 tests intencionalmente inestables y corrige cada uno. Documenta la causa raiz y la solucion.

Ejercicio 2: Construir Sistema de Cuarentena

Crea mecanismo de tag @Quarantine, configura CI para ejecutar tests en cuarentena separadamente, construye script de seguimiento.

Ejercicio 3: Pipeline de Prevencion

Agrega testing en modo repeticion para tests nuevos, configura orden aleatorio, crea dashboard de inestabilidad.