No está listo hasta que el navegador lo confirma
Un cambio pasó todas nuestras pruebas aunque dependía de un campo que el backend nunca enviaba. Por eso hacemos que un navegador real compruebe lo que creemos.
Por Erick Agrazal, Fundador
Una vez publicamos una corrección que aparecía en verde por todas partes y no hacía nada.
El cambio leía un campo de la respuesta de una API y lo mostraba. Las pruebas unitarias pasaron. Las respuestas simuladas incluían el campo con valores creíbles. La integración continua estaba en verde. La revisión no encontró nada porque el código era correcto frente a todo lo que habíamos decidido comprobar.
El backend no enviaba ese campo. Nunca lo había enviado.
Nadie cometió un error por descuido. El autor creía que la API devolvía el campo, escribió el código a partir de esa idea y luego creó respuestas simuladas que repetían la misma idea. Las pruebas no cuestionaban la suposición. Estaban hechas de ella. Añadir más pruebas iguales solo habría levantado una pared verde más alta, no una más útil.
Las pruebas son un circuito cerrado
Ese es el problema de fondo. Conviene decirlo sin rodeos porque resulta incómodo: una suite escrita junto al código comparte muchas de sus suposiciones. Cuando la suposición está equivocada, la suite puede estar de acuerdo con mucho entusiasmo.
Las respuestas simuladas lo hacen más evidente. Cada una es una afirmación escrita sobre un sistema que no estás ejecutando. Pero no es el único caso. Estos ejemplos de nuestra propia historia también necesitaron algo fuera del circuito para aparecer.
Nuestro camino rápido de pruebas usa SQLite en lugar de Postgres. SQLite no aplica las llaves foráneas por defecto. Un error de referencias pasó la suite rápida y solo apareció cuando corrimos las mismas pruebas contra una base de datos real.
Nuestros datos de prueba creaban las cuentas y sus transacciones en el mismo momento. En la vida real, una cuenta suele existir antes que el historial que alguien importa. Varios errores en los límites de fechas permanecieron invisibles hasta que usamos cuentas más antiguas que sus movimientos.
Hay otro caso que todavía me incomoda. Durante una refactorización cambiamos el nombre de una prueba y, sin querer, también debilitamos lo que comprobaba. El nombre seguía describiendo la versión anterior y más estricta. La suite continuó en verde mientras la cobertura empeoraba. Lo descubrimos al ejecutar el cuerpo de la prueba anterior contra el código nuevo.
Por eso tiene que mirar un navegador
Cada tarde, nuestro proceso de QA publica el trabajo integrado en un entorno real y lo recorre con un navegador real. Entra con una cuenta real, abre la función que cambió y mira lo que aparece en pantalla. También lo hace en español. Una clave de traducción perdida no existe para una prueba que nunca renderiza la página.
No hay nada sofisticado en esto. Ese es precisamente el valor. El navegador no comparte las ideas del autor, no lee las respuestas simuladas y consulta la API real. Si el backend no envía el campo, queda un espacio vacío. Ese espacio vacío es un hallazgo.
Nuestra regla es simple: solo aceptamos como evidencia la función publicada haciendo lo que debe hacer. Un chequeo verde no basta. Tampoco basta con que un agente diga que terminó. Un resumen seguro de sí mismo es el resultado más barato de producir en todo el sistema.
Cuando una tarea falla durante la validación real, retiramos sus cambios de la rama de integración y la devolvemos a la cola. El resto del lote sigue avanzando. Aislar cada fallo hace que el control sea práctico. Si una sola falla detuviera toda la entrega, aparecería la tentación de dejarla pasar.
Saber cuáles fallos son reales
Lo difícil de validar en vivo no es el navegador. Los entornos reales también producen ruido, y confundirlo con un defecto puede comerse una mañana completa.
El ejemplo más claro fue un grupo de pruebas que fallaba siempre al ejecutarse junto y pasaba siempre por separado. Nuestra primera sospecha fue estado compartido. Buscamos datos que se escaparan entre archivos y no encontramos nada. Las pruebas corrían en paralelo y se quitaban CPU entre ellas en una máquina cargada hasta superar sus propios tiempos límite. La misma selección, ejecutada en serie, pasó cada vez. La corrección fue dar más margen al tiempo límite, no añadir aislamiento.
Otro caso aparece justo después de reconstruir un servicio. Durante unos segundos, el sitio puede responder con un 404 mientras el contenedor reinicia. Ya lo hemos visto varias veces. La respuesta correcta es reintentar durante un periodo definido y comprobar si los demás servicios están sanos. Si la API y el tablero responden, ese 404 breve suele ser parte del arranque, no una rotura.
Ambos casos exigen la misma habilidad: distinguir un resultado equivocado de uno ruidoso. Perseguir un fantasma cuesta tiempo. Aprender a ignorar cualquier señal roja destruye el control.
Lo que cuesta
La validación de la tarde es la parte más lenta de nuestro proceso y la más molesta de mantener. La automatización del navegador se rompe por motivos que no tienen nada que ver con el producto. De vez en cuando, un cambio correcto vuelve a la cola y se corrige otra vez a la mañana siguiente.
Aun así, no la quitaría. Todos los demás controles comparan un resultado con una regla. Este compara el resultado con el mundo. Es el único que podía descubrir un campo que nunca existió.
Eso deja una pregunta, la misma que me hacen durante la cena.