Pruebas inestables: qué las causa y cómo solucionarlas para siempre

  1. Inicio
  2. Blog
  3. Automatización de pruebas

Automatización de pruebas

Pruebas inestables: Qué las causa y cómo solucionarlas definitivamente

Una prueba que falla al azar le enseña a su equipo a ignorar las fallas. Aprenda cómo confirmar que una prueba no es correcta, localizar la causa real y solucionarla, con demostraciones que muestran lo que realmente funciona.

9 min de lectura
Pruebas inestables: qué las causa y cómo solucionarlas para siempre

Puntos clave

  • Una prueba inestable pasa y falla en el mismo código, sin cambios.
  • Las causas habituales son el tiempo, el estado compartido, los datos inestables, los servicios externos y las diferencias ambientales.
  • Reemplace las inactividades fijas con esperas para una condición específica y proporcione a cada prueba sus propios datos.
  • Los reintentos pueden ocultar errores reales. Ponga en cuarentena una prueba deficiente, corríjala dentro de un plazo o elimínela.

La compilación se pone roja, alguien hace clic en “volver a ejecutar”, pasa y todos siguen adelante. Parece inofensivo, pero las pruebas deficientes destruyen lentamente lo más valioso que tiene un conjunto de pruebas: la confianza de la gente en él. Cuando las fallas suelen ser ruido, los errores reales también se detectan.

Esta guía explica por qué las pruebas se vuelven inestables y cómo solucionar cada causa adecuadamente. Ejecutamos las demostraciones de sincronización y estado compartido en Python 3.13, y los números que se muestran provienen de esas ejecuciones. Son pequeñas demostraciones diseñadas para hacer que la descamación sea fácil de ver, por lo que sus propias tasas de fracaso serán diferentes.

¿Qué es una prueba escamosa?

Una prueba inestable da resultados diferentes en el mismo código. Pasa en una ejecución y falla en la siguiente, aunque no haya cambiado nada en el producto. La prueba puede tener un error o el código puede contener un error intermitente real. Esa segunda posibilidad es exactamente la razón por la que no deberías simplemente descartarlas.

¿Por qué las pruebas inestables son tan costosas?

  • Confianza perdida. Una vez que las personas asumen que el rojo significa “probablemente inestable”, dejan de investigar fallas reales.
  • Tiempo perdido. Los desarrolladores pasan horas volviendo a ejecutar canalizaciones y examinando fallas que no tienen nada que ver con su cambio.
  • Entrega más lenta. Fusiona y libera la espera en las reposiciones, lo que socava el objetivo de CI/CD.
  • Errores ocultos. Las condiciones de carrera en el producto pueden parecerse al ruido de prueba, y esos son los que perjudican en la producción.

Causas comunes de un vistazo

causa Signo típico Solución habitual
Temporización y asíncrono Falla más cuando CI está ocupado Espere una condición
Estado compartido Falla según el orden de la prueba Aislar datos, restablecer estado
Fechas y aleatoriedad Falla en determinados momentos o días Controla el reloj y la semilla.
Selectores frágiles Falla después de cambios en la interfaz de usuario Utilice localizadores estables
Servicios externos Falla cuando una dependencia es lenta Burlarse de ellos o aplastarlos
Diferencias ambientales Pasa localmente, falla en CI Alinear con contenedores
Límites de recursos Falla en ejecuciones paralelas Sintonizar a los trabajadores, arreglar la contención

Causa 1: sincronización y sueño fijo

Esta es la causa más común. Una prueba supone que el trabajo en segundo plano finalizará después de un retraso fijo. Para ver el efecto, escribimos una pequeña prueba en la que el trabajo tarda hasta 2 ms, mientras que la prueba espera solo 1 ms antes de verificar:

def flaky_timing():
    result = {}
    def work():
        time.sleep(random.uniform(0, 0.002))
        result["done"] = True
    threading.Thread(target=work).start()
    time.sleep(0.001)        # hope it has finished
    assert result.get("done")

La solución es esperar la condición real, con un límite superior generoso:

def fixed_timing():
    result = threading.Event()
    def work():
        time.sleep(random.uniform(0, 0.002))
        result.set()
    threading.Thread(target=work).start()
    assert result.wait(timeout=1)   # continues as soon as it is ready

Ejecutamos cada versión 200 veces:

Versión Fallos de 200
Sueño fijo 102
Espere una condición 0

El resultado es más que solo confiabilidad. La versión fija avanza en el momento en que se realiza el trabajo, por lo que suele ser también más rápida. Una suspensión más prolongada haría que la primera versión fallara con menos frecuencia, pero cada ejecución sería más lenta y aún podría fallar en una máquina de CI ocupada.

En las pruebas de navegadores se aplica el mismo principio. Utilice la espera automática de Dramaturgo, o Esperas de selenio explícitas, en lugar de time.sleep:

# Playwright: retries until true or a timeout is reached
expect(page.get_by_text("Order placed")).to_be_visible()

# Selenium: an explicit wait for a condition
WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.ID, "confirmation"))
)

Causa 2: estado compartido y orden de prueba

Cuando las pruebas comparten datos, una puede afectar a la otra y el resultado depende del orden en que se ejecutan. Estas dos pruebas utilizan una lista compartida:

store = []

def test_add():
    store.append("x")
    assert len(store) == 1

def test_empty():
    assert store == []
Orden test_add test_empty
vacío, luego agregue Pase Pase
agregar, luego vaciar Pase fallar

Nuestra ejecución confirmó esto, y es por eso que una suite puede pasar meses y luego fallar cuando alguien agrega ejecuciones paralelas o un complemento de orden aleatorio. La cura es el aislamiento:

  • Proporcione a cada prueba datos nuevos, utilizando un dispositivo que los configure y los limpie. Nuestro tutorial de pytest muestra cómo hacerlo.
  • Cree registros únicos, como un correo electrónico con un sufijo aleatorio, en lugar de un “usuario de prueba” compartido.
  • Envuelva las pruebas de la base de datos en una transacción y revertirlas, donde su configuración lo permita.
  • Realice pruebas en orden aleatorio con regularidad, para que estos problemas aparezcan temprano.

Causa 3: Fechas, zonas horarias y aleatoriedad

Un examen que se aprueba toda la semana puede suspender un sábado, a medianoche o el último día del mes. Haga del tiempo una entrada, en lugar de leer el reloj dentro de su lógica:

def is_weekend(today=None):
    today = today or datetime.date.today()
    return today.weekday() >= 5

is_weekend(datetime.date(2026, 11, 7))   # True  (a Saturday)
is_weekend(datetime.date(2026, 11, 9))   # False (a Monday)

Esas pruebas dan la misma respuesta cualquier día. Del mismo modo, establezca una semilla fija para valores aleatorios en las pruebas y regístrela cuando utilice datos aleatorios para poder reproducir una falla. No asuma un orden particular a partir de conjuntos, diccionarios de fuentes externas o consultas de bases de datos sin ORDER BY.

Causa 4: Selectores frágiles

Una prueba que encuentra un botón con div:nth-child(3) > span falla con cualquier ajuste de diseño y puede fallar solo para algunos tamaños o variantes de pantalla. Prefiera localizadores basados ​​en lo que ven los usuarios o atributos de prueba dedicados. Nuestra guía de Localizadores de dramaturgos explica el orden para probarlos. Las animaciones son otra fuente frecuente de problemas, así que considere desactivarlas en entornos de prueba.

Causa 5: Servicios y redes externos

Si una prueba llama a un proveedor de pagos real o a una API de terceros, entonces su resultado depende del tiempo de actividad y los límites de velocidad de otra persona. Simule esas llamadas para la mayoría de las pruebas y mantenga una pequeña cantidad de verificaciones de contrato separadas contra el servicio real, para poder saber si cambia. Nuestra guía para pruebas unitarias versus pruebas de integración explica cómo dividirlas.

Consejo: “Aprueba localmente, falla en CI” normalmente significa una diferencia en velocidad, zona horaria, recursos o versiones. La ejecución de pruebas en la misma imagen de contenedor localmente y en CI elimina muchas de ellas. Nuestro tutorial de Docker Compose puede resultar útil.

Cómo diagnosticar una prueba escamosa

  1. Confírmelo. Ejecute la prueba muchas veces sin cambios. Con pytest, funciona un bucle de shell simple y un complemento como pytest-repeat le brinda --count=100.
  2. Registrar evidencia. Guarde registros, capturas de pantalla y seguimientos de cada falla. El visor de huellas de Dramaturgo es particularmente útil aquí.
  3. Enfatiza las condiciones. Ejecútalo con CPU limitada, en paralelo o en orden aleatorio. Hacer que se suspenda con más frecuencia hace que sea más fácil estudiar.
  4. Pruébelo solo. Si siempre pasa solo pero falla en la suite, sospeche del estado compartido.
  5. Busque patrones. Compruebe qué máquinas, horas del día y pruebas vecinas están involucradas.
  6. Compruebe el producto. Pregunte honestamente si esto podría ser una condición de carrera real en su aplicación.

¿Los reintentos automáticos son una solución?

Los reintentos son una red de seguridad útil a corto plazo y no son una cura. Un único reintento automático en CI puede mantener el trabajo en movimiento mientras usted investiga, pero una prueba que necesita un reintento para pasar todavía le dice algo. Peor aún, un error de producto real e intermitente que falla el 20% de las veces normalmente pasará desapercibido en un reintento.

Utilice reintentos con estas reglas: limítelos a uno o dos, registre cada prueba que necesite una y trate esa lista como su lista de tareas pendientes. Una prueba informada como “aprobada al reintentar” debería contar como un problema a investigar, no como un éxito.

Un proceso de equipo para pruebas inestables

  1. Medir. Realice un seguimiento de qué pruebas fallan y luego pase la misma confirmación. Muchas herramientas de CI y reporteros de pruebas pueden demostrar esto.
  2. Cuarentena. Saque una prueba inestable confirmada de la ejecución de bloqueo, para que deje de interrumpir a todos, pero continúe ejecutándola y rastreándola.
  3. Asigna un propietario y una fecha límite. Una cuarentena sin fecha límite se convierte en un cementerio.
  4. Reparar o eliminar. Si no vale la pena arreglar una prueba, elimínela y verifique que su cobertura aún exista en otros lugares.
  5. Prevenir. Ejecute nuevas pruebas varias veces antes de fusionarlas. Una prueba que falla una vez cada cincuenta carreras se detecta mejor ahora que en seis meses.

Trate una prueba inestable como un error con prioridad y no como ruido de fondo. La gravedad y la prioridad también se aplican aquí, como se explica en gravedad frente a prioridad. Si la inestabilidad es generalizada, lea sobre errores de automatización de pruebas más amplios y considere si tiene demasiadas pruebas a nivel del navegador.

Conclusión

Las pruebas inestables parecen aleatorias, pero rara vez lo son. Casi todo el mundo se reduce a una suposición sobre el tiempo, el orden, los datos o el entorno que no siempre es cierta. Elija la prueba que falle con más frecuencia en su proceso esta semana. Ejecútelo cincuenta veces, encuentre la suposición y arréglelo para siempre. Una solución real le enseña a un equipo que más de cien reposiciones lo harán.

Preguntas frecuentes

¿Qué es una prueba escamosa?

Una prueba inestable pasa algunas ejecuciones y falla en otras sin ningún cambio en el código. La causa puede estar en la prueba, el medio ambiente o el producto.

¿Qué causa las pruebas inestables?

Las causas más comunes son suposiciones de tiempo, estado compartido entre pruebas, selectores inestables, servicios externos, manejo de fechas y diferencias entre CI y máquinas locales.

¿Debo eliminar las pruebas inestables?

Primero intenta arreglarlos. Si una prueba tiene poco valor y es costosa de arreglar, eliminarla está bien, pero asegúrese de que su cobertura importante exista en otro lugar.

¿Debo volver a intentar las pruebas fallidas automáticamente?

Como red de seguridad limitada, sí, pero registre cada reintento e investíguelo. Los reintentos por sí solos ocultan los problemas y pueden permitir que errores intermitentes reales lleguen a producción.

¿Cómo encuentro pruebas inestables?

Busque pruebas que fallen y aprueben la misma confirmación, utilizando su historial de CI o una herramienta de informes. También puede ejecutar pruebas sospechosas muchas veces en un bucle.

sigue aprendiendo

Trabajar con Stack Quora

¿Necesitas ayuda con pruebas, WordPress o SEO?

Ayudamos a los equipos a enviar mejor software y hacer crecer sus sitios. Cuéntenos en qué está trabajando y le sugeriremos un siguiente paso claro.

Sigue leyendo

Más guías sobre testing, IA y desarrollo moderno

¿Te gustó este artículo? Elige un tema y salta directo a nuestros tutoriales más recientes.

Inteligencia artificial

Explicación de las alucinaciones de la IA: por qué los modelos de lenguaje inventan cosas y cómo reducirlas

Se explicaron las alucinaciones de la IA: qué son, por qué los modelos de lenguaje las producen, tipos comunes, cómo reducirlas con conexión a tierr…

Leer artículo →
DevOps

Explicación de las variables de entorno: archivos .env, secretos y configuración bien hechos

Variables de entorno explicadas con ejemplos de Python que ejecutamos: leerlas y configurarlas, por qué cada valor es una cadena, archivos .env, her…

Leer artículo →
Desarrollo de software

Explicación de la limitación de velocidad de API: algoritmos, encabezados y manejo de errores 429

La limitación de la tasa de API se explica con una simulación que ejecutamos: depósito de tokens, ventana fija, ventana deslizante, 429 respuestas, …

Leer artículo →
Automatización de pruebas

Burlarse en Python: unittest.mock, patch y los errores que hacen que las pruebas mientan

Burlarse en Python con unittest.mock, probado: Mock y return_value, side_effect, parches donde se usa un nombre, especificación y autospec, fechas d…

Leer artículo →
Pruebas de software

Explicación de la cobertura del código: cobertura de línea versus sucursal y lo que realmente significa 100%

Cobertura del código explicada con una ejecución real de cobertura.py: cobertura de línea, rama y condición, lectura de un informe, por qué una cobe…

Leer artículo →
Bases de datos y SQL

DELETE vs TRUNCATE vs DROP en SQL: diferencias, reversión y cuándo usar cada uno

DELETE vs TRUNCATE vs DROP explicado con SQL probado: qué elimina cada uno, reversión, activadores, restablecimiento de identidad, velocidad y en qu…

Leer artículo →
Bases de datos y SQL

Vistas SQL explicadas: cree, use y evite los errores, con ejemplos

Vistas SQL explicadas con consultas que ejecutamos: creación de una vista, resultados en vivo, ocultación de columnas, rendimiento, vistas actualiza…

Leer artículo →
Desarrollo de software

Interfaz TypeScript versus tipo: diferencias reales, probadas con el compilador

Interfaz TypeScript versus tipo, verificado con el compilador real: qué pueden hacer ambos, qué puede hacer solo uno, fusión de declaraciones, exten…

Leer artículo →
Desarrollo de software

Python *args y **kwargs explicados: funciones flexibles con ejemplos claros

Python *args y **kwargs explicados con el código que ejecutamos: empaquetado y desempaquetado, orden de parámetros, argumentos solo de palabras clav…

Leer artículo →
Desarrollo de software

Funciones Lambda de Python: sintaxis, usos reales y cuándo no utilizarlas

Funciones lambda de Python explicadas con el código que ejecutamos: sintaxis, clasificación con clave, mapa y filtro, cierres, trampa de enlace tard…

Leer artículo →
Inteligencia artificial

¿Qué son los agentes de IA? Cómo funcionan, con un bucle de agente simple que puedes ejecutar

¿Qué son los agentes de IA? Una guía sencilla sobre el bucle del agente, las herramientas, la memoria y las barreras de seguridad, con un pequeño bu…

Leer artículo →
DevOps

Monitoreo versus observabilidad: la verdadera diferencia, con registros, métricas y seguimientos

Explicación del monitoreo versus observabilidad: fallas conocidas y desconocidas, registros, métricas y seguimientos, un recorrido por incidentes de…

Leer artículo →
DevOps

Docker Volumes versus Bind Mounts: persistir los datos del contenedor de la manera correcta

Se explican los volúmenes de Docker frente a los montajes de enlace: por qué desaparecen los datos del contenedor, volúmenes con nombre, montajes de…

Leer artículo →
Automatización de pruebas

TDD vs BDD: diferencias clave, una función creada en ambos sentidos y cuándo usar cada una

TDD vs BDD explicado con una característica de descuento construida en ambos sentidos: refactor rojo-verde con el código que ejecutamos, escenarios …

Leer artículo →
Pruebas de software

Pruebas de Aceptación de Usuario (UAT): Proceso, Lista de Verificación y Ejemplos Reales

Explicación de las pruebas de aceptación del usuario: quién las ejecuta, en qué se diferencia del control de calidad, un proceso de siete pasos, una…

Leer artículo →
Bases de datos y SQL

Valores NULL de SQL explicados: IS NULL, COALESCE, NULLIF y las trampas que se deben evitar

Los valores NULL de SQL se explican con las consultas que ejecutamos: lógica de tres valores, IS NULL, COALESCE, NULLIF, cómo los agregados, las uni…

Leer artículo →
Bases de datos y SQL

Declaración SQL CASE WHEN: sintaxis, ejemplos reales y errores comunes

CASO SQL CUANDO se explicó con las consultas que ejecutamos: formularios simples y buscados, orden de condiciones, pivotes con SUMA, trucos COUNT, c…

Leer artículo →
Desarrollo de software

¿Qué es JWT? Tokens web JSON explicados con un ejemplo práctico

¿Qué es JWT? JSON Web Tokens explicó: las tres partes, un token creado y manipulado en el código que ejecutamos, JWT vs sesiones, almacenamiento, to…

Leer artículo →
Desarrollo de software

Explicación del bucle de eventos de JavaScript: pila de llamadas, microtareas y temporizadores con ejemplos

El bucle de eventos de JavaScript explicado con el código que ejecutamos: pila de llamadas, colas de tareas y microtareas, por qué setTimeout 0 no e…

Leer artículo →
Desarrollo de software

Explicación de los generadores y el rendimiento de Python: ahorre memoria con ejemplos reales

Los generadores de Python y el rendimiento se explican con el código que ejecutamos: cómo se pausan y reanudan, midieron el ahorro de memoria, canal…

Leer artículo →
Inteligencia artificial

¿Qué son las incrustaciones? Los vectores y la similitud del coseno se explican de forma sencilla

¿Qué son las incrustaciones? Una guía en lenguaje sencillo sobre vectores, similitud de cosenos y búsqueda semántica, con un pequeño ejemplo que eje…

Leer artículo →
DevOps

Implementación azul-verde versus implementación canaria: diferencias, compensaciones y cómo elegir

Explicación de la implementación azul-verde versus canaria: cómo funciona cada uno, costo, riesgo y reversión, agrupación estable de usuarios que pr…

Leer artículo →
Automatización de pruebas

Pruebas inestables: qué las causa y cómo solucionarlas para siempre

Pruebas inestables explicadas con demostraciones que realizamos: las causas principales, cómo confirmar y diagnosticar una, correcciones adecuadas p…

Leer artículo →
Pruebas de software

Pruebas de carga frente a pruebas de estrés: diferencias, métricas y ejemplos de k6

Explicación de las pruebas de carga frente a las pruebas de estrés: qué prueba cada una, pruebas de picos y de absorción, las métricas que importan,…

Leer artículo →
Pruebas de software

Caso de prueba frente a escenario de prueba: diferencias clave, ejemplos y cuándo utilizar cada uno

Caso de prueba versus escenario de prueba explicado: definiciones claras, una tabla en paralelo, una función de restablecimiento de contraseña tomad…

Leer artículo →
Bases de datos y SQL

Optimización de consultas SQL: lea planes de ejecución y corrija consultas lentas, con tiempos

Optimización de consultas SQL probada en un millón de filas: lea planes de ejecución, agregue los índices correctos, corrija filtros no sargables, u…

Leer artículo →
Bases de datos y SQL

SQL UNION vs UNION ALL: diferencias, ejemplos y rendimiento probado

SQL UNION vs UNION ALL explicado con consultas que ejecutamos: duplicados, las reglas que siguen ambos, un error de datos real, rendimiento medido, …

Leer artículo →
Desarrollo de software

Tutorial de JavaScript Fetch API: GET, POST, manejo de errores y tiempos de espera

Un tutorial de JavaScript Fetch API probado en un servidor local: GET y POST, por qué 404 no arroja, un asistente reutilizable, cadenas de consulta,…

Leer artículo →
Desarrollo de software

Manejo de archivos Python: lectura, escritura, CSV, JSON y pathlib con ejemplos

El manejo de archivos Python se explica con el código que ejecutamos: modos abiertos, lectura de archivos grandes, CSV, JSON, pathlib, archivos bina…

Leer artículo →
Desarrollo de software

Clases y objetos de Python: tutorial de programación orientada a objetos con ejemplos reales

Clases y objetos de Python explicados con el código que ejecutamos: __init__, métodos, atributos de clase versus instancia, herencia, propiedades, m…

Leer artículo →
Inteligencia artificial

¿Qué es un modelo de lenguaje grande? Cómo funcionan los LLM, explicados sin exageraciones

¿Qué es un modelo de lenguaje grande? Los LLM explicados en lenguaje sencillo: tokens, incrustaciones, atención, entrenamiento, ventanas de temperat…

Leer artículo →
DevOps

Comandos de Linux para principiantes: la guía esencial de terminal con ejemplos

Comandos de Linux para principiantes, agrupados por tarea: navegación, archivos, visualización de registros, grep, canalizaciones, permisos, proceso…

Leer artículo →
Automatización de pruebas

Selenium vs Playwright: diferencias clave, comparación de códigos y cuál elegir

Comparación de Selenium vs Playwright: arquitectura, espera, soporte de navegador e idioma, depuración, la misma prueba en ambas herramientas, conse…

Leer artículo →
Pruebas de software

Partición de equivalencia: técnica de diseño de pruebas explicada con ejemplos resueltos

Explicación de la partición de equivalencia: cómo encontrar particiones válidas e inválidas, ejemplos prácticos para edades, contraseñas y cupones, …

Leer artículo →
Pruebas de software

Severidad vs Prioridad en Pruebas de Software: Diferencias, Matriz y Ejemplos Reales

Explicación de gravedad versus prioridad en las pruebas de software: definiciones claras, una matriz de cuatro vías con ejemplos, escalas de niveles…

Leer artículo →
Bases de datos y SQL

Tutorial de subconsultas SQL: tipos, subconsultas correlacionadas y EXISTENCIAS con ejemplos

Un tutorial de subconsultas SQL con consultas que ejecutamos: subconsultas escalares, IN, FROM y SELECT, subconsultas correlacionadas, EXISTS, la tr…

Leer artículo →
Bases de datos y SQL

Transacciones SQL y ACID explicados: COMMIT, ROLLBACK y niveles de aislamiento

Transacciones SQL y ACID explicadas con una transferencia bancaria que ejecutamos: COMMIT, ROLLBACK, puntos de guardado, bloqueos, niveles de aislam…

Leer artículo →
Desarrollo de software

Git merge vs rebase: diferencias, ejemplos y cuándo usar cada uno

Git merge vs rebase explicado con salida real del log, una tabla comparativa, la regla de oro del rebase y un flujo de trabajo seguro para equipos.

Leer artículo →
Desarrollo de software

List comprehension en Python: sintaxis, ejemplos y cuándo evitarla

List comprehension en Python explicada: sintaxis, filtros, if-else, bucles anidados, tiempos medidos y cuándo es mejor un bucle for normal.

Leer artículo →