QA · Lectura de 8 min

Pruebas de integración: cuando las piezas por separado sí, pero juntas no

Cada módulo pasa sus unitarias y aun así el sistema falla. El bug vive en la costura entre componentes, y ahí solo lo atrapa la integración.

Una prueba de integración verifica que dos o más componentes que funcionan bien por separado también funcionan bien cuando se conectan. No examina una función aislada, como la unitaria, sino la costura entre piezas: la llamada de un módulo a otro, la consulta que va a la base de datos, la respuesta que llega desde un servicio externo. El error que busca no vive dentro de un componente, sino en el espacio entre ellos.

Esa es la respuesta corta. La respuesta útil es entender por qué existe este nivel intermedio de prueba, por qué no basta con tener cada módulo bien probado por su cuenta, y cuándo conviene pagar su costo. Porque la integración cuesta más que la unitaria: es más lenta, más frágil y más difícil de diagnosticar. La pregunta no es si usarla, sino dónde.

01 · la costuraQué prueba la integración que la unitaria no puede ver

Recuerda la pirámide de pruebas. En la base van muchas unitarias, rápidas y aisladas. En el medio, menos pruebas de integración. En la punta, pocas de extremo a extremo. La integración ocupa el segundo nivel porque su alcance es intermedio: más que una unidad, menos que el sistema completo.

El problema que resuelve es concreto. Una prueba unitaria examina una función sin dependencias reales: para hacerlo, reemplaza todo lo que la rodea con sustitutos controlados. Eso es exactamente lo que la hace rápida y confiable, pero también lo que la vuelve ciega a un tipo de fallo. Si el módulo A asume que el módulo B devuelve una fecha en un formato, y B en realidad la devuelve en otro, las unitarias de A y de B pasan las dos. Cada una probó su lado del contrato con un sustituto que respetaba la suposición equivocada. El sistema falla solo cuando A y B se hablan de verdad.

Un bug de integración no está dentro de ningún componente. Está en la suposición que un componente hace sobre otro.

A eso se le llama contrato: el acuerdo implícito o explícito sobre qué envía un componente y qué espera recibir. Formato de datos, orden de los campos, qué pasa cuando algo sale mal, cuánto tarda una respuesta. La prueba de integración es, en el fondo, una verificación de que ambos lados entienden el contrato de la misma manera. La ISTQB define la prueba de integración precisamente como la que se enfoca en las interacciones entre componentes o sistemas [1].

Figura 1 · el bug vive en la costura
Módulo A unitarias: OK Módulo B unitarias: OK el contrato aquí falla Cada módulo pasa sus pruebas en aislamiento. La conexión, no.
A espera una fecha en un formato; B la manda en otro. Sus unitarias pasan porque cada una probó su lado con un sustituto que respetaba la suposición equivocada. Solo la integración toca la costura donde vive el error.

02 · la decisiónDobles de prueba o dependencias reales

Aquí aparece la decisión que define una prueba de integración: ¿usas el componente real al otro lado de la costura, o un sustituto controlado? A esos sustitutos se les llama dobles de prueba, por analogía con los dobles de riesgo del cine. Hay varios tipos, y conviene no mezclar los nombres: un stub devuelve respuestas fijas preparadas de antemano; un mock además verifica que se le llamó como esperabas; un fake es una implementación ligera pero funcional, como una base de datos en memoria en vez de la real. Gerard Meszaros ordenó y nombró estos patrones en su catálogo de xUnit [2].

La regla práctica es simple de enunciar y difícil de aplicar: usa dependencias reales cuando lo que quieres probar es justamente la integración con esa dependencia, y usa dobles cuando esa dependencia es solo ruido para la prueba que te importa.

Si estás probando que tu código guarda y recupera bien datos de una base, usa una base de datos real, o al menos un fake fiel, porque el punto de la prueba es esa conversación. Reemplazar la base por un stub que siempre dice "guardado" no probaría nada: estarías probando el stub. En cambio, si estás probando la lógica de un módulo que, de paso, envía un correo, no levantes un servidor de correo de verdad: pon un doble que registre que se intentó enviar. El correo no es lo que estás verificando.

Cuantos más dobles metes, más rápida y estable es la prueba, y menos se parece a la realidad. Ese es el intercambio, siempre.

El riesgo de abusar de los dobles

Un doble codifica una suposición sobre cómo se comporta el componente real. Si esa suposición está mal, o queda desactualizada cuando el componente cambia, tu prueba sigue en verde mientras el sistema real ya está roto. Un test que pasa sobre un supuesto falso es peor que no tener test: da confianza sin respaldo. Por eso las dependencias reales, aunque más caras, siguen siendo insustituibles justo en las costuras que más te importan.

03 · el costoPor qué detectar tarde un fallo de integración sale caro

Todo esto tiene una razón económica de fondo. El costo de arreglar un defecto crece a medida que avanza por las etapas del desarrollo. Un fallo que atrapas al escribir el código cuesta poco: lo corriges en minutos, con todo el contexto fresco en la cabeza. El mismo fallo descubierto en producción, después de pasar por integración, pruebas de sistema y despliegue, cuesta mucho más: hay que reproducirlo, rastrear qué componentes intervienen, coordinar el arreglo y volver a desplegar. Esta idea, de que el costo de la corrección escala con la distancia entre la introducción del defecto y su detección, es un principio clásico de la ingeniería de software [3].

Los fallos de integración son especialmente caros de detectar tarde por una razón: son difíciles de atribuir. Cuando el sistema completo falla, no sabes si la culpa es de A, de B o de cómo se hablan. El síntoma aparece lejos de la causa. Un buen conjunto de pruebas de integración acorta esa distancia: en vez de descubrir el problema cuando un usuario reporta algo raro, lo descubres cuando la prueba que ejercita esa costura concreta se pone en rojo. Sabes qué dos piezas dejaron de entenderse.

Figura 2 · el costo crece con la demora
Código Integración Sistema Producción costo de arreglar
Cuanto más lejos de su origen se detecta un defecto de integración, más cuesta atribuirlo y corregirlo. Las pruebas de integración empujan la detección hacia la izquierda, donde arreglar todavía es barato.

04 · el equilibrioCuándo usarlas y cuántas conviene tener

La forma de la pirámide ya trae la respuesta: pruebas de integración conviene tener menos que unitarias y más que de extremo a extremo. No porque valgan menos, sino porque cada una cuesta más de escribir y de mantener, y tarda más en correr. Si intentas cubrir todo con integración, terminas con una suite lenta y frágil que el equipo deja de ejecutar. Si no tienes ninguna, cada despliegue es una apuesta sobre costuras que nadie verificó.

La guía práctica es priorizar por riesgo. Escribe pruebas de integración en las costuras donde un fallo sería caro o difícil de detectar: la frontera con la base de datos, la conversación con un servicio de pago, el punto donde tu código recibe datos de un tercero que no controlas. Deja fuera lo que ya cubre bien una unitaria: la lógica interna de un módulo no necesita una base de datos real para ser verificada.

La prueba de integración no reemplaza a la unitaria ni a la de extremo a extremo. Se sitúa entre ambas para cubrir el punto ciego de las dos: el lugar donde piezas correctas, cada una por su cuenta, dejan de entenderse cuando por fin se conectan. Ese punto ciego es real, es frecuente, y es caro si lo descubres tarde. Por eso este nivel de la pirámide existe.

Fuentes

  1. International Software Testing Qualifications Board (ISTQB). Certified Tester Foundation Level (CTFL) Syllabus, v4.0 (2023). Sección sobre niveles de prueba: definición de integration testing como enfoque en las interacciones entre componentes o sistemas. istqb.org.
  2. Meszaros, G. (2007). xUnit Test Patterns: Refactoring Test Code. Addison-Wesley. Catálogo de dobles de prueba (Test Double): stub, mock, fake, spy y dummy.
  3. International Organization for Standardization. ISO/IEC/IEEE 12207:2017, Systems and software engineering: Software life cycle processes. Marco de referencia sobre procesos de verificación y detección temprana de defectos a lo largo del ciclo de vida.

Aprende más sobre IA

Ver todo Aprende IA
WhatsApp