3. Diferencias entre pruebas unitarias, de integración, de sistema y end-to-end

3.1 Introducción

En testing existen distintos niveles de prueba. Cada nivel observa el software desde una distancia diferente y responde preguntas distintas. Comprender esas diferencias es importante para no esperar de una prueba algo que corresponde a otro nivel.

En este tema compararemos cuatro niveles muy habituales: pruebas unitarias, pruebas de integración, pruebas de sistema y pruebas end-to-end.

El foco principal de este curso son las pruebas de integración, pero para entenderlas bien necesitamos ubicarlas dentro del conjunto completo de pruebas.

3.2 Idea general de los niveles

Podemos imaginar los niveles de prueba como distintos grados de amplitud:

  • Las pruebas unitarias miran una pieza pequeña.
  • Las pruebas de integración miran la conexión entre piezas.
  • Las pruebas de sistema miran el producto completo.
  • Las pruebas end-to-end miran un recorrido completo desde el punto de vista del usuario o de un proceso externo.
Cuanto más amplio es el nivel de prueba, más realista suele ser el escenario, pero también aumenta el costo de preparación, ejecución y diagnóstico.

3.3 Pruebas unitarias

Una prueba unitaria verifica una unidad pequeña de código de forma aislada. Esa unidad puede ser una función, un método, una clase o un módulo simple. El objetivo es comprobar una lógica concreta sin depender de bases de datos, redes, archivos o servicios externos.

Por ejemplo, una prueba unitaria puede verificar que una función calcule correctamente el descuento de una compra o que una validación rechace un correo electrónico inválido.

Sus características principales son:

  • Son rápidas de ejecutar.
  • Son fáciles de repetir muchas veces.
  • Aíslan la lógica que se quiere probar.
  • Facilitan encontrar el origen de un error.
  • No demuestran que el sistema integrado funcione completo.

3.4 Pruebas de integración

Una prueba de integración verifica que dos o más componentes colaboren correctamente. A diferencia de una prueba unitaria, aquí sí nos interesa que exista interacción entre partes del sistema.

Por ejemplo, una prueba de integración puede comprobar que un servicio reciba una solicitud, consulte una base de datos, aplique una regla de negocio y guarde el resultado esperado.

Sus características principales son:

  • Verifican comunicación entre componentes.
  • Detectan errores de contratos, datos, configuración y persistencia.
  • Pueden usar algunas dependencias reales y otras simuladas.
  • Son más lentas que muchas pruebas unitarias.
  • Dan confianza sobre colaboraciones importantes del sistema.

3.5 Pruebas de sistema

Las pruebas de sistema evalúan el sistema completo como producto. El objetivo es verificar si cumple los requisitos definidos, tanto funcionales como no funcionales, en un ambiente representativo.

Por ejemplo, en un sistema de ventas, una prueba de sistema puede revisar que sea posible administrar productos, registrar clientes, generar pedidos, consultar reportes y aplicar reglas generales del negocio.

Sus características principales son:

  • Evalúan el comportamiento del sistema completo.
  • Se apoyan en requisitos funcionales y no funcionales.
  • Pueden incluir pruebas manuales y automatizadas.
  • Requieren un ambiente más completo que una prueba unitaria o de integración pequeña.
  • Si fallan, puede requerirse más análisis para ubicar la causa.

3.6 Pruebas end-to-end

Las pruebas end-to-end, también llamadas E2E, verifican un flujo completo de principio a fin. Observan el sistema desde la perspectiva de un usuario, un cliente externo o un proceso que atraviesa varias partes del producto.

Por ejemplo, una prueba end-to-end puede simular que un usuario inicia sesión, busca un producto, lo agrega al carrito, paga y recibe una confirmación.

Sus características principales son:

  • Son las más cercanas a un uso real del sistema.
  • Validan recorridos completos y críticos.
  • Pueden involucrar interfaz gráfica, backend, base de datos y servicios externos.
  • Suelen ser más lentas y frágiles que otros niveles.
  • No conviene usarlas para cubrir todos los detalles internos.

3.7 Comparación rápida

La siguiente tabla resume las diferencias principales:

Nivel Foco Velocidad Ejemplo
Unitaria Una unidad aislada. Muy alta. Calcular el impuesto de una venta.
Integración Colaboración entre componentes. Media. Guardar una venta y actualizar stock.
Sistema Producto completo contra requisitos. Media o baja. Verificar el módulo completo de ventas.
End-to-end Flujo completo desde el punto de vista del usuario. Baja. Comprar un producto desde la pantalla hasta la confirmación.

3.8 Qué tipo de errores detecta cada nivel

Cada nivel tiene fortalezas distintas. No todos detectan los mismos problemas con la misma claridad.

Tipo de error Nivel donde suele detectarse mejor
Una fórmula matemática incorrecta. Prueba unitaria.
Un servicio envía un campo con nombre incorrecto. Prueba de integración.
Una funcionalidad completa no cumple un requisito. Prueba de sistema.
El usuario no puede completar una compra desde la interfaz. Prueba end-to-end.
Una variable de entorno apunta a una base de datos incorrecta. Prueba de integración o de sistema.

3.9 Un mismo ejemplo en cuatro niveles

Supongamos una funcionalidad que permite crear una orden de compra. Podemos probarla en distintos niveles:

  • Prueba unitaria: verificar que el cálculo del total sume productos, impuestos y descuentos correctamente.
  • Prueba de integración: verificar que el servicio de órdenes guarde la orden y descuente stock en la base de datos.
  • Prueba de sistema: verificar que el módulo de compras permita crear, consultar, cancelar y listar órdenes según los requisitos.
  • Prueba end-to-end: verificar que un usuario pueda iniciar sesión, elegir productos, pagar y recibir la confirmación.

El mismo negocio se observa desde perspectivas diferentes. Ninguna de estas pruebas reemplaza completamente a las demás.

3.10 Por qué no probar todo solo con end-to-end

Como las pruebas end-to-end se parecen mucho al uso real, puede parecer tentador cubrir todo con ellas. Sin embargo, eso suele traer problemas.

Las pruebas end-to-end suelen ser más lentas, dependen de más partes y pueden fallar por causas muy distintas. Si una prueba completa falla, el problema puede estar en la interfaz, el backend, la base de datos, la red, un dato de prueba o un servicio externo.

Por eso es mejor combinar niveles. Las pruebas unitarias detectan errores pequeños con rapidez; las de integración validan colaboraciones; las de sistema revisan requisitos; y las end-to-end confirman flujos críticos completos.

3.11 Por qué no probar todo solo con unitarias

También sería un error depender únicamente de pruebas unitarias. Aunque sean rápidas y valiosas, no verifican que el sistema conectado funcione correctamente.

Un proyecto puede tener muchas pruebas unitarias exitosas y aun así fallar porque:

  • La aplicación no puede conectarse a la base de datos.
  • Dos servicios interpretan distinto el mismo campo.
  • Una consulta SQL no coincide con el esquema real.
  • Un endpoint externo cambió su respuesta.
  • Una configuración necesaria falta en el ambiente.

Las pruebas de integración existen precisamente para cubrir ese espacio entre unidades aisladas y sistema completo.

3.12 El lugar de las pruebas de integración

Las pruebas de integración son el puente entre la lógica aislada y el comportamiento completo del sistema. Su lugar es especialmente importante cuando varias partes deben coordinarse para producir un resultado.

Son útiles para responder preguntas como:

  • ¿El controlador llama al servicio correcto?
  • ¿El servicio guarda datos con el formato esperado?
  • ¿La base de datos devuelve lo que el código necesita?
  • ¿El mensaje publicado en una cola puede ser consumido por otro componente?
  • ¿El sistema maneja correctamente una respuesta de error de una dependencia?

3.13 Relación con la pirámide de pruebas

La pirámide de pruebas es una idea usada para representar una estrategia equilibrada. En general, propone tener muchas pruebas unitarias, una cantidad intermedia de pruebas de integración y menos pruebas end-to-end.

No es una regla matemática, pero ayuda a pensar el balance. Si hay demasiadas pruebas amplias y pocas pruebas pequeñas, la suite puede volverse lenta e inestable. Si solo hay pruebas pequeñas, se pierde confianza sobre el sistema conectado.

La clave no es cumplir una proporción exacta, sino usar cada nivel para lo que mejor verifica.

3.14 Cómo decidir el nivel adecuado

Para decidir en qué nivel probar un caso, podemos hacernos estas preguntas:

  • ¿Quiero verificar una regla interna simple? Probablemente convenga una prueba unitaria.
  • ¿Quiero verificar que dos partes se comuniquen bien? Conviene una prueba de integración.
  • ¿Quiero validar una funcionalidad completa contra requisitos? Puede corresponder una prueba de sistema.
  • ¿Quiero confirmar un flujo crítico desde la perspectiva del usuario? Puede corresponder una prueba end-to-end.
  • ¿Necesito rapidez, diagnóstico claro o máximo realismo? Esa respuesta influye en el nivel elegido.

3.15 Qué debes recordar de este tema

  • Las pruebas unitarias verifican unidades pequeñas de código.
  • Las pruebas de integración verifican la colaboración entre componentes.
  • Las pruebas de sistema evalúan el producto completo contra requisitos.
  • Las pruebas end-to-end recorren flujos completos desde una perspectiva externa o de usuario.
  • Ningún nivel reemplaza por completo a los demás.
  • El nivel adecuado depende del riesgo, del objetivo y del costo de ejecución.

3.16 Conclusión

Los niveles de prueba no compiten entre sí: se complementan. Una estrategia madura usa pruebas unitarias para validar lógica aislada, pruebas de integración para comprobar colaboraciones, pruebas de sistema para evaluar requisitos y pruebas end-to-end para confirmar recorridos críticos.

En este curso nos centraremos en el nivel de integración, porque allí aparecen muchos defectos importantes relacionados con contratos, datos, configuración, persistencia y dependencias.

En el próximo tema veremos con más detalle qué significa integrar componentes de software y cómo reconocer los límites entre las partes que deben colaborar.