Una tasa del 5% de tests inestables en un suite de 1.000 tests que corre 20 veces al día en CI cuesta entre 15 y 20 horas de ingeniería por semana en re-ejecuciones e investigación, y eso sin contar la erosión de confianza: una vez que los ingenieros descartan los fallos como "probablemente inestables", los bugs reales se publican. La arquitectura de auto-waiting de Playwright produce tasas de inestabilidad del 12% versus el 28% de Selenium en los equipos encuestados, porque cada clic y aserción espera a que los elementos estén listos en lugar de requerir utilidades de espera manuales. Este artículo cuantifica el costo total, explica la diferencia arquitectónica y muestra cómo identificar y corregir la inestabilidad que ya tienes.
Qué cuestan realmente los tests inestables
Un test inestable es uno que produce resultados diferentes con el mismo código: a veces pasa, otras veces falla, sin ninguna razón determinística.
El costo visible: un ingeniero ve un test fallido, lo vuelve a ejecutar, pasa, lo marca como "inestable" y continúa. Cinco minutos perdidos.
El costo invisible es mayor:
Pérdida de confianza en el suite. Una vez que los ingenieros saben que algunos tests son inestables, empiezan a tratar los fallos como "probablemente inestables" por defecto. Los fallos reales se descartan. Los bugs se publican. El suite de tests se ha convertido en ruido. Sobrecarga de re-ejecuciones. Un suite que tiene un 10% de tests inestables y está configurado para reintentar 3 veces está ejecutando efectivamente el 130% de sus tests en cada ejecución de CI. En un suite de 30 minutos, eso son 9 minutos por ejecución, horas al día en todo un equipo. Tiempo de desarrolladores en investigación. Cada nuevo fallo requiere que alguien decida: ¿esto es inestable o es real? Esa decisión lleva entre 5 y 30 minutos de investigación. Con 10 fallos inestables por día, eso es una hora de tiempo de ingeniería diaria. Releases demorados. Si tu CI está configurado para bloquear el despliegue cuando hay fallos de tests (como debería estar), los tests inestables se convierten en bloqueantes de release. El workaround (deshabilitar los tests) elimina la cobertura en la que confiabas. Estimación cuantificada: Un equipo con un 5% de tasa de inestabilidad en un suite de 1.000 tests, ejecutando CI 20 veces al día, probablemente gasta entre 15 y 20 horas de ingeniería por semana en overhead relacionado con la inestabilidad. A 60 dólares la hora de costo cargado, eso son entre 900 y 1.200 dólares por semana, o entre 50.000 y 60.000 dólares por año.Esto no es teórico. Google publicó un estudio en 2016 que mostraba que 1 de cada 7 tests en su monorepo presentaba inestabilidad en algún momento. Los números en la mayoría de las empresas son peores, no mejores.
Las causas más comunes de tests inestables
Antes de comparar frameworks, entiende qué causa realmente la inestabilidad:
Race conditions contra la UI asíncrona. El test hace clic en un botón y luego verifica inmediatamente un resultado que todavía no cargó. El timing funciona en una máquina rápida, falla en un servidor de CI más lento. Estado de test compartido. El test A crea datos, el test B los lee, el test C los elimina. Si corren en un orden diferente o en paralelo, se interfieren. Timing del entorno. El test pasa localmente (SSD rápido, red rápida) pero falla en CI (máquina más lenta, latencia de red hacia el entorno de test). Delays de animaciones. Una animación CSS está completándose mientras el test intenta interactuar con el elemento animado. Timing de red. Una llamada a la API tarda 500ms en promedio pero ocasionalmente tarda 2000ms. El timeout del test está configurado en 1000ms. Dependencias externas. Los tests que hablan con servicios de terceros reales (procesadores de pago, proveedores de email) son inestables cuando esos servicios tienen problemas.Cómo la arquitectura de Playwright reduce la inestabilidad
Playwright fue diseñado desde cero pensando en el problema de la inestabilidad. Los dos mecanismos clave:
Auto-waiting
Cada acción y aserción de Playwright espera automáticamente a que el elemento objetivo esté en el estado correcto antes de continuar.
page.click() espera a que el elemento:
- Exista en el DOM
- Sea visible
- No esté cubierto por otro elemento
- No esté animándose
- Esté habilitado
expect(locator).toBeVisible() espera hasta el timeout configurado para que la aserción se vuelva verdadera.
Esto elimina la fuente más común de inestabilidad: la race condition de "el elemento existe pero todavía no está listo". No agregas llamadas waitForSelector en todas partes. Solo escribes click y Playwright maneja la espera.
Aserciones web-first
La librería de aserciones de Playwright (expect) está construida para UIs asíncronas. Cuando escribes:
await expect(page.getByText('Éxito')).toBeVisible();Esto no toma una instantánea del estado y lo verifica una vez. Consulta el DOM repetidamente hasta que la aserción se vuelve verdadera, o hasta que expira el timeout. El timeout por defecto es de 5 segundos.
Compáralo con una aserción ingenua:
// Inestable: verifica el estado en este momento exacto, no cuando se vuelve verdadero
const text = await page.textContent('.message');
expect(text).toBe('Éxito');La versión de Playwright reintenta; la versión ingenua no. La mayor parte de la inestabilidad en los suites basados en Selenium viene del patrón ingenuo, porque Selenium no tiene aserciones con reintento integradas.
Playwright vs Selenium: comparación de inestabilidad
Acá es donde la diferencia arquitectónica se muestra en números reales.
La comparación pública más completa es el reporte Checkly State of Testing 2024, que encuestó a unos 1.500 equipos de ingeniería:
- Los equipos que usan Playwright reportaron el 12% de sus tests como regularmente inestables
- Los equipos que usan Selenium reportaron el 28% como regularmente inestables
- Los equipos que usan Cypress reportaron el 18% como regularmente inestables
La metodología importa: "regularmente inestable" significa que el equipo identificó el test como inestable y está trabajando alrededor de él. Muchos más tests son intermitentemente inestables pero todavía no fueron marcados.
La diferencia se reduce a algunos aspectos específicos:
Selenium requiere esperas explícitas.driver.findElement(By.id("result")) falla inmediatamente si el elemento no está presente. Los equipos de Selenium experimentados agregan llamadas WebDriverWait en todas partes. Los equipos sin experiencia no lo hacen, o no lo hacen consistentemente, y los tests son inestables.
Los waits por defecto de Playwright son apropiados para las SPA modernas. Las aplicaciones React y Vue renderizan de forma asíncrona. El auto-waiting de Playwright fue diseñado para esto. Selenium fue diseñado para páginas tradicionales renderizadas en el servidor y adaptado a las SPA; la adaptación se nota.
La interceptación de red de Playwright está integrada. Mockear respuestas de API para hacer los tests determinísticos es una función de primera clase en Playwright. En Selenium requiere herramientas adicionales (BrowserMob Proxy, etc.) que agregan complejidad y sus propios modos de fallo.
El costo real de la inestabilidad de Selenium en la práctica
Un equipo que migró de Selenium a Playwright en una empresa SaaS de tamaño mediano documentó su experiencia:
Antes de la migración:
- 1.200 tests de Selenium
- ~180 tests marcados como "conocidamente inestables" (15%)
- Tiempo de ejecución de CI: 45 minutos con 3 reintentos
- ~8 horas de ingeniería por semana en investigación de inestabilidad
- Tests deshabilitados por inestabilidad: 47 (4%)
Después de la migración a Playwright (6 meses después):
- 1.200 tests migrados, ~200 tests nuevos agregados
- ~40 tests marcados como inestables (2,5%)
- Tiempo de ejecución de CI: 22 minutos con 1 reintento
- ~1 hora de ingeniería por semana en inestabilidad
- Tests deshabilitados: 5 (0,3%)
La reducción en el tiempo de ejecución vino en parte de que Playwright es más rápido (ejecución paralela, sin overhead de WebDriver), pero en gran parte de necesitar menos reintentos.
Cómo medir tu costo actual de inestabilidad
Antes de corregir la inestabilidad, mídela. La mayoría de los sistemas de CI lo rastrean si sabes dónde mirar.
GitHub Actions: revisa los artefactos del reporte de tests. La mayoría de los reporters de Playwright incluyen un estado "flaky" para los tests que pasaron en el reintento. Reporter HTML de Playwright: agrega enplaywright.config.ts:
reporter: [['html', { open: 'never' }]],El reporte HTML marca los tests que pasaron en el reintento. Esos son tus tests inestables.
Seguimiento manual: ejecuta tu suite de tests 10 veces contra el mismo commit. Cualquier test que no produzca el mismo resultado cada vez es inestable. Es tedioso pero definitivo.Calcular el costo
1. Cuenta el número de tests inestables únicos
2. Estima el tiempo promedio de investigación por fallo (generalmente 10 a 20 minutos)
3. Estima con qué frecuencia falla cada test inestable por semana
4. Multiplica: (fallos de tests inestables por semana) × (minutos por investigación) / 60 = horas de ingeniería por semana
Para la mayoría de los equipos, este número es sorprendente.
Corregir tests inestables en Playwright
Cuando tienes tests inestables en un suite de Playwright, el enfoque de diagnóstico:
Paso 1: Correr con trace habilitado
// playwright.config.ts
use: {
trace: 'on-first-retry',
}En el reintento, Playwright graba un trace completo: capturas de pantalla, solicitudes de red, snapshots del DOM, output de la consola. Ábrelo con npx playwright show-trace trace.zip.
Paso 2: Identificar el punto de fallo
El trace muestra exactamente dónde falló el test y cuál era el estado de la página. Generalmente el elemento todavía no era visible, la solicitud de red no había completado, o el test anterior dejó un estado que afectó a este.
Paso 3: Corregir con aserciones web-first
Reemplaza los patrones frágiles:
// Frágil
await page.waitForTimeout(2000);
await page.click('.submit-button');
// Robusto
await page.getByRole('button', { name: 'Enviar' }).click();
// El auto-waiting maneja el timingPaso 4: Corregir el estado compartido
Cada test debe configurar sus propios datos y no depender de otros tests. Usa hooks beforeEach y llamadas a la API para crear el estado que cada test necesita.
test.beforeEach(async ({ request }) => {
// Crear un usuario nuevo para cada test vía API
await request.post('/api/users', { data: { email: 'test@example.com' } });
});FAQ
Tenemos 500 tests de Selenium. ¿Vale la pena migrar a Playwright?Para la mayoría de los equipos: sí, si la inestabilidad es un problema real. El tiempo de migración es aproximadamente de 2 a 4 semanas de tiempo de ingeniería para 500 tests, dependiendo de la complejidad. Los ahorros continuos (menos tiempo de CI, menos tiempo de investigación, suite más confiable) típicamente lo justifican en pocos meses. Consultá la guía de migración para el marco de decisión.
Nuestros tests de Playwright siguen siendo inestables. ¿Qué estamos haciendo mal?La mayor parte de la inestabilidad en Playwright viene del estado de test compartido (tests que dependen entre sí) o de evitar el auto-waiting con llamadas waitForTimeout. Buscá setTimeout y waitForTimeout en tus archivos de tests. Reemplazá cada uno con una aserción apropiada.
Por debajo del 2% es alcanzable. Por debajo del 1% para un suite bien mantenido es realista. Cero es el objetivo; 0,5% es bueno. Por encima del 5% significa que la inestabilidad está afectando la productividad de tu equipo y el suite está perdiendo credibilidad.
¿El auto-waiting de Playwright elimina todas las race conditions?No. Elimina las más comunes (elemento no listo para la interacción, aserción fallida porque el resultado no apareció todavía). Las race conditions en la lógica de tus tests (tests que dependen entre sí, tests que comparten estado de base de datos) no las resuelve el framework. Esas requieren aislamiento de tests.
→ See also: Depurando Tests Inestables: Una Guía Práctica | Playwright vs Cypress vs Selenium en 2026: Comparación Honesta