Probando rutas y flujos reales: pruebas de integración en el backend
Cuando la lógica de negocio ya está protegida con pruebas unitarias, el siguiente paso natural es comprobar qué ocurre cuando esa lógica se expone al mundo real.
En backend, ese “mundo real” empieza en las rutas HTTP.
Una aplicación backend no es una suma de funciones sueltas. Es un sistema donde varias capas cooperan de forma continua:
- Rutas de Express
- Middlewares de validación, autenticación y errores
- Lógica de negocio
- Persistencia en base de datos
- Serialización de respuestas
Las pruebas de integración existen para comprobar que todas estas piezas encajan y trabajan juntas de forma correcta.
Las pruebas unitarias comprueban que cada mecánica del juego funciona por separado.
Las pruebas de integración comprueban que el nivel completo funciona cuando el jugador entra, se mueve, combate y guarda la partida.
Qué valida realmente una prueba de integración
Una prueba de integración no busca detectar errores internos muy finos. Su objetivo es otro: validar flujos reales de uso, tal y como los vive un cliente.
En una aplicación backend típica, este tipo de prueba responde a preguntas como:
- ¿La ruta devuelve el código HTTP correcto?
- ¿Los datos se validan antes de procesarse?
- ¿La lógica de negocio se ejecuta en el orden esperado?
- ¿La base de datos refleja el cambio?
- ¿La respuesta tiene la forma que espera el cliente?
Aquí ya no se prueba “una función”, sino un contrato externo: lo que la API promete hacer.
Supertest: el puente entre tests y Express
Para ejecutar pruebas de integración sin complejidad innecesaria se utiliza Supertest.
Supertest permite simular peticiones HTTP directamente contra una aplicación Express sin levantar un servidor real ni abrir puertos.
Esto aporta ventajas muy claras:
- Las pruebas son más rápidas
- No hay conflictos de puertos
- El entorno está totalmente controlado
- El código de producción no se modifica
El test no “arranca el servidor”. Importa la aplicación Express y actúa como si fuera un cliente HTTP.
Es como probar un nivel del juego cargándolo directamente en modo depuración, sin pasar por el menú principal ni por el arranque completo.
Diferencia real entre prueba unitaria y prueba de integración
La diferencia no está en la herramienta, sino en el alcance de lo que se prueba.
Prueba unitaria
- Ejecuta una función concreta
- No conoce HTTP ni Express
- No usa base de datos real
- Falla cuando una regla interna es incorrecta
Prueba de integración
- Ejecuta una ruta completa
- Usa HTTP simulado
- Pasa por middlewares y persistencia
- Falla cuando las piezas no encajan bien
Ambos tipos de prueba son necesarios. Cada uno protege un nivel distinto del sistema.
Arquitectura mínima para integrar sin dolor
Para que las pruebas de integración sean fiables, la aplicación debe estar bien organizada.
Separación entre app y server
La aplicación Express debe exportarse sin escuchar en un puerto. El listen vive en otro archivo. Así, los tests pueden importar la app directamente.
Configuración por entorno
El entorno de testing debe activar comportamientos específicos:
- Base de datos de prueba
- Logs reducidos
- Datos iniciales controlados
Esto suele hacerse con variables como NODE_ENV=test.
Base de datos aislada
Nunca se prueba contra datos reales.
Lo habitual es:
- Usar SQLite en memoria
- Recrear el esquema antes de cada test
- Limpiar el estado tras cada ejecución
Sin esto, las pruebas se vuelven frágiles y peligrosas.
Flujo mental de una prueba de integración
Aunque el código cambie, el esquema conceptual siempre es el mismo.
Preparar entorno
- App Express
- Base de datos limpia
- Datos iniciales (fixtures)
Ejecutar petición HTTP
- Método (GET, POST, PUT, DELETE)
- Ruta
- Headers
- Body
Verificar respuesta
- Código HTTP
- Estructura JSON
- Valores concretos
Verificar efectos secundarios
- Datos guardados
- Estado modificado
Este flujo replica cómo interactúan realmente los clientes con el backend. Por eso las pruebas de integración son el puente entre la lógica interna y el uso real del sistema.
Ejemplo conceptual de prueba de integración
Sin entrar en sintaxis concreta, el razonamiento sería:
Pseudoflujo:
DADO un backend con base de datos vacía
Y un usuario válido existente
CUANDO envío POST /login con credenciales correctas
ENTONCES:
- recibo HTTP 200
- recibo un token
- el cuerpo tiene la estructura esperada
Aquí no importa cómo se genera el token ni qué algoritmo se usa. Importa que el contrato de la API se cumple.
Casos típicos que deben cubrirse en integración
Una buena batería de pruebas de integración suele incluir:
- Rutas felices
- peticiones válidas
- datos correctos
- respuestas exitosas
- Errores controlados
- datos inválidos
- recursos inexistentes
- permisos insuficientes
- Estados límite
- listas vacías
- duplicados
- valores fuera de rango
- Autenticación y autorización
- acceso con token
- acceso sin token
- acceso con rol incorrecto
Estos casos no suelen detectarse en pruebas unitarias, pero sí aparecen en uso real.
Pruebas de integración y regresión
Cada prueba de integración define un comportamiento que no debe romperse en el tiempo. Cuando el backend evoluciona, estas pruebas actúan como pruebas de regresión automática.
Si un cambio rompe una ruta existente:
- La prueba falla
- El error se detecta antes de producción
- El contrato se protege
Este efecto es uno de los mayores valores del testing de integración.
Coste y estrategia
Las pruebas de integración son más lentas que las unitarias. Por eso no se usan para todo.
Estrategia habitual:
- Muchas pruebas unitarias (rápidas)
- Menos pruebas de integración (clave)
- Cobertura sobre flujos críticos
Este equilibrio maximiza confianza sin penalizar el desarrollo diario.
Por qué este nivel es imprescindible
Sin pruebas de integración, un backend puede pasar todos los tests unitarios y aun así fallar en producción. Los errores más costosos suelen aparecer en la frontera entre capas.
Las pruebas de integración son las que confirman que:
lo que funciona bien por separado, también funciona bien en conjunto.
Cuando este nivel está bien cubierto, el backend deja de ser una caja negra frágil y se convierte en un sistema predecible, verificable y preparado para crecer.