Más allá del happy path: errores, timeouts y red caída
Artículo publicado el 11/05/2026 por Marc Galindo.
La mayoría de bugs que llegan a producción no están en el happy path. Están en los estados que nadie diseñó: la pantalla cuando la API tarda demasiado, el formulario después de un error de red, la lista cuando el servidor responde con un 500…
Sin embargo, cuando un tester recibe una tarea, lo primero —y muchas veces lo único— que prueba es el flujo principal. El usuario hace clic en el botón, los datos llegan, la pantalla se actualiza. Verde. Listo.
El problema es que el happy path es la parte más probada del software. La ha probado el desarrollador mientras lo construía, el PM cuando lo validó y QA cuando lo recibió. Si algo va a fallar, no va a fallar ahí. Va a fallar en lo que nadie ha visto.
Por qué los estados de error se escapan
Hay tres motivos que se repiten en casi todos los equipos:
- No están en los criterios de aceptación. Los criterios suelen describir qué debe ocurrir cuando todo sale bien. Rara vez especifican qué debe ocurrir cuando algo falla, porque escribir ese tipo de criterios obliga a pensar en cosas que nadie quiere pensar.
- No están en los mockups. El diseñador entrega tres pantallas: la vista vacía, la vista con datos, y a veces la de carga. La pantalla de error casi nunca está, y cuando está, suele ser una pantalla genérica que no refleja el contexto real del fallo.
- No están en el entorno de pruebas. Es difícil reproducir un timeout cuando el entorno no tiene carga de usuarios y responde en 200 milisegundos. Es difícil ver el estado de “sin conexión” cuando la conexión funciona. Probar errores requiere provocarlos, y eso pocos lo hacen.
El resultado es predecible: el estado de error existe en el código (porque alguien ha tenido que implementarlo), pero nadie ha visto cómo se comporta de verdad. Y cuando un usuario lo ve por primera vez, lo descubrimos a través de un ticket de soporte.
Una taxonomía mínima de errores que probar
No hace falta una clasificación académica. Hace falta una lista corta que un tester pueda recorrer con cualquier funcionalidad y preguntarse, en cada punto, “¿qué pasa si…?”. En mi experiencia, cuatro categorías cubren la inmensa mayoría de casos.
Errores de red
Son los más comunes y los menos probados. El usuario no tiene cobertura, está en un ascensor o metro, su VPN se desconecta a mitad de una petición, su router se reinicia o experimenta pérdida de paquetes. La aplicación no recibe respuesta porque la petición nunca llegó al servidor.
Lo que suele fallar aquí no es que la aplicación no funcione: es que miente. Muestra un spinner infinito, deja un botón deshabilitado para siempre, o peor, asume que la petición ha tenido éxito y actualiza la interfaz sin que los datos se hayan guardado realmente. El usuario cree que ha enviado el formulario y al volver a la pantalla descubre que no.
Las preguntas clave: si la petición nunca llega al servidor, ¿el usuario lo sabe? ¿Puede reintentar? ¿Pierde lo que había escrito? ¿Se le notifica de alguna forma o se queda con la duda de si su acción tuvo efecto o no?
Errores de servidor (5xx)
El servidor responde, pero responde mal. Un 500, un 502, un 503. La diferencia con el caso anterior es importante: la conexión funciona, la petición ha llegado, pero el servidor no ha podido procesarla. La aplicación tiene una respuesta, solo que es una respuesta de error.
Aquí lo más habitual es que el frontend ignore por completo el código de estado. Trata cualquier respuesta como si fuera un 200 e intenta parsear un cuerpo que no tiene la forma esperada, lo que provoca un error de JavaScript en el cliente. El usuario ve una pantalla en blanco o un mensaje genérico que no le ayuda a entender qué ha pasado ni qué puede hacer.
Las preguntas clave: si el servidor responde con un 5xx, ¿la aplicación lo detecta como error o sigue ejecutándose como si todo fuera bien? ¿Si se muestra un mensaje de error, es útil para el usuario? ¿Se le permite reintentar sin perder los datos que había introducido?
Timeouts
Una variante peligrosa de los anteriores, y la más fácil de pasar por alto en pruebas. La petición sale, llega al servidor, el servidor empieza a procesarla, pero tarda demasiado (o, más bien, más de lo que el desarrollador creía que podría llegar a tardar). El cliente no sabe si la operación se ha completado o no.
Este caso es especialmente problemático en operaciones que modifican datos. Si un timeout ocurre durante un pago, ¿el cargo se ha hecho? ¿Hay que reintentar? ¿El reintento va a duplicar el cargo? Es uno de los pocos escenarios donde un bug puede tener consecuencias económicas directas, y aun así muchos productos no lo prueban.
Las preguntas clave: si la respuesta no llega en un tiempo razonable, ¿qué hace la aplicación? ¿Aborta la petición? ¿Hay una política de reintentos? ¿Se queda esperando indefinidamente? ¿Avisa al usuario?
Errores de validación
A diferencia de los anteriores, estos no son fallos del sistema sino respuestas legítimas del servidor diciendo “lo que has enviado no es válido”. Un 400, un 422. El servidor funciona perfectamente, pero los datos que recibe incumplen alguna regla.
El bug clásico aquí no es que la validación no funcione. Es que el mensaje de error que devuelve el servidor no se procesa correctamente y no se muestra al usuario, o se muestra de forma genérica (“ha ocurrido un error”) cuando el servidor estaba devolviendo información útil (“el email ya está registrado”). Otro caso muy típico: la validación del frontend y la del backend no coinciden, así que el usuario consigue enviar el formulario pero el servidor lo rechaza por reglas que el frontend no comprueba (por ejemplo, un formato de email, código postal o NIF incorrecto).
Las preguntas clave: si el servidor rechaza la petición por una razón concreta, ¿el usuario ve esa razón o ve un mensaje genérico que no le permite corregir o incluso entender el problema? ¿El frontend tiene las mismas reglas de validación que el backend para evitar rechazos inesperados?
Probar estos cuatro tipos de error no es opcional según la criticidad de la funcionalidad. Lo es según el coste de que fallen en producción. Una operación de pago, un registro de usuario, una subida de archivo o el guardado de un formulario largo merecen que cada uno de los cuatro estados se pruebe explícitamente, aunque añada tiempo al sprint.
Cómo provocar estos errores en pruebas
El mayor obstáculo para probar estados de error no es el tiempo: es la dificultad de reproducirlos. En un entorno de pruebas que funciona, los errores no ocurren de forma natural. Hay que provocarlos.
Para pruebas manuales, las herramientas de desarrollador del navegador son suficientes en la mayoría de casos. La pestaña Network permite simular conexión lenta, sin conexión, o interceptar peticiones individuales y modificar su respuesta. No hace falta un entorno especial, solo saber dónde está el botón.
Para pruebas automatizadas, Playwright permite mockear las respuestas de red con muy poco código. Este es un ejemplo mínimo de cómo verificar que la aplicación se comporta correctamente cuando el servidor responde con un 500:
import { test, expect } from '@playwright/test'
test('muestra mensaje de error cuando la API responde con 500', async ({
page,
}) => {
// Interceptar la llamada y forzar un 500
await page.route('**/api/products', (route) =>
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' }),
}),
)
await page.goto('/products')
// Verificar que el usuario ve un mensaje útil, no una pantalla en blanco
await expect(page.getByRole('alert')).toContainText(
'No se han podido cargar los productos',
)
// Verificar que puede reintentar sin tener que recargar la página
await expect(page.getByRole('button', { name: 'Reintentar' })).toBeVisible()
})
El mismo patrón sirve para los demás casos: cambiar status: 500 por status: 408 simula un timeout, llamar a route.abort() simula una caída de red, y devolver un cuerpo con errores de validación permite verificar que esos mensajes llegan correctamente al usuario.
Estas pruebas no sustituyen a las pruebas del happy path; las complementan. Por cada flujo crítico, conviene tener al menos una prueba que verifique el camino feliz y otra que verifique cómo se comporta la aplicación cuando ese flujo falla en algún punto, debido a su importancia para el negocio.
Una checklist para tus próximas pruebas
Si quieres incorporar este enfoque sin reorganizar tu proceso de pruebas, recorre esta lista cada vez que recibas una tarea que implique una llamada al servidor. No tardarás más de unos minutos en revisarla, y cubrirás los escenarios donde se concentran la mayoría de defectos en producción:
- Sin conexión: desconecta la red antes de la acción. ¿Sabe el usuario qué ha pasado? ¿Puede reintentar?
- Conexión lenta: usa slow 3G en las herramientas del navegador. ¿La interfaz se queda colgada o muestra un estado de carga claro?
- 5xx: intercepta la petición y devuelve un 500. ¿Aparece un mensaje útil o una pantalla en blanco?
- Timeout: aborta la petición tras varios segundos. ¿La aplicación detecta que ha pasado demasiado tiempo o se queda esperando indefinidamente?
- Validación: envía datos inválidos. ¿El mensaje de error es el que el servidor devuelve o uno genérico?
- Doble envío: pulsa el botón dos veces seguidas. ¿La aplicación bloquea el segundo envío o ejecuta la operación dos veces?
- Reintento: tras un error, ¿es posible reintentar sin perder los datos que el usuario había introducido, o se vacía el formulario?
No es una lista exhaustiva. Es lo mínimo. Y si solo tienes tiempo para una de las siete, que sea la primera: la red caída es el escenario más común y, paradójicamente, el menos probado.
Probar el camino feliz verifica que la aplicación funciona. Probar los estados de error verifica que la aplicación es honesta con el usuario cuando algo no funciona. Y esa diferencia, en producción, marca la línea entre un bug que el usuario perdona y un bug que le hace cerrar la aplicación para no volver.
El happy path es donde el software es bonito. Los estados de error son donde el software se gana —o pierde— la confianza. Cuando un cliente llega a una web para comprar un producto, espera que la compra se complete sin problemas. Eso transmite confianza. Pero si algo falla (debemos admitir que puede pasar), lo que el cliente espera no es que la aplicación se quede colgada o le muestre un mensaje críptico. Lo que espera es que la aplicación le diga claramente qué ha pasado, por qué ha pasado y qué puede hacer para solucionarlo. Eso va a marcar la diferencia entre un cliente que se va frustrado y uno que, a pesar del error, sigue confiando en la marca.