20 de enero de 2025

Cómo diseñar pruebas de integración escalables usando mocks en proyectos reales

Introducción

Las pruebas de integración son fundamentales en proyectos de software modernos, pero diseñarlas de manera que escalen con el crecimiento del proyecto es un desafío común. En este artículo, exploraremos cómo usar mocks de manera efectiva para crear suites de pruebas que no solo funcionen, sino que permanezcan mantenibles y rápidas a medida que tu proyecto crece.

A diferencia de las pruebas unitarias puras, las pruebas de integración verifican que múltiples componentes trabajen juntos correctamente. Sin embargo, cuando estas pruebas dependen de recursos externos (bases de datos, APIs, servicios de terceros), pueden volverse lentas, frágiles e impredecibles. Aquí es donde los mocks juegan un papel crucial.

Qué problema resuelven realmente los mocks

Los mocks no son solo "simulaciones" de componentes reales. Su verdadero valor radica en resolver problemas específicos de las pruebas de integración:

Aislamiento de dependencias externas

Cuando tu aplicación depende de servicios externos (APIs de pago, servicios de email, bases de datos), las pruebas no deberían fallar porque un servicio externo está caído o es lento.

Control del comportamiento

Los mocks permiten simular casos extremos que serían difíciles o costosos de reproducir con sistemas reales: timeouts, errores específicos, respuestas malformadas, etc.

Velocidad de ejecución

Las pruebas con mocks son órdenes de magnitud más rápidas que aquellas que requieren levantar bases de datos, contenedores Docker o llamar a APIs reales.

Ejecuta en cualquier sistema

Con mocks, los tests pueden ejecutarse en cualquier sistema, eliminando la necesidad de configurar un ambiente de prueba específico. Esto es útil para tests de CI/CD.

Arquitectura típica que NO escala

Antes de hablar de soluciones, identifiquemos un anti-patron común que conduce a suites de pruebas frágiles:

Acoplamiento directo a implementaciones

Cuando tus pruebas conocen detalles internos de cómo se implementan los componentes, cualquier refactorización rompe las pruebas, incluso si el comportamiento externo no cambió.

Diseño de pruebas de integración escalables: Ejemplo práctico

Un diseño escalable de pruebas de integración se basa en principios arquitectónicos sólidos. Para demostrar estos conceptos en acción, he implementado una arquitectura completa en un proyecto real de gestión de usuarios con Spring Boot 3.x y Java 17.

Arquitectura de 6 componentes

La arquitectura que recomiendo consta de 6 componentes clave que trabajan juntos para crear tests mantenibles y rápidos. Cada uno tiene una responsabilidad específica siguiendo principios SOLID como se puede visualizar a continuación:

tests/integration/
├── InfraBase          # Configuración base compartida
├── api/
│   └── ApiClient      # Contrato HTTP del sistema
├── builders/
│   └── DataBuilder    # Construcción de datos de prueba
├── flows/
│   └── BusinessFlows  # Orquestación de casos de uso
├── mocks/
│   └── ExternalMock   # Simulación de dependencias
└── IntegrationTests   # Suite de tests finales

Esta arquitectura aplica tres principios clave que garantizan la escalabilidad:

  • 1. Responsabilidades claras: Cada capa cumple un rol específico, evitando acoplamientos.

  • 2. Fácil de extender: Se agregan nuevos tests y flujos sin romper lo existente.

  • 3. Integraciones aisladas: Dependencias externas siempre mockeadas para pruebas estables.

Aplicaremos los principios anteriores en el siguiente proyecto de ejemplo que usamos para Chaining Prompts con GitHub Copilot.

Una vez finalizada la implementación, se pueden ejecutar las pruebas y verificar que todas pasan correctamente.

Pruebas funcionando correctamente

Pruebas funcionando bajo la estructura expuesta

Código completo del proyecto

💻 Código Completo en GitHub

El proyecto incluye la implementación completa de la arquitectura de 6 componentes: InfraIT, UserApi, UserBuilder, UserFlows, UserRepositoryMock y UserIT con 10 tests funcionales. Este es el proyecto migrado con Spring Boot 3.x y Java 17.

Ver Repositorio en GitHub

Qué encontrarás en el repositorio

  • Estructura completa de 6 componentes implementados según la arquitectura escalable

  • 10 tests de integración funcionales de 3-5 líneas cada uno

  • API REST con Spring Boot lista para ejecutar y probar

  • Documentación detallada de cada componente y su responsabilidad

  • Carpeta de instrucciones con los prompts utilizados para la implementación

Métricas de Escalabilidad

Si tu arquitectura de pruebas implementa correctamente los principios anteriores, deberías obtener estas métricas:

📊 Métricas de Escalabilidad

  • Nuevo test: ~5 líneas

  • Nuevo endpoint: 2-3 archivos

  • Nuevo campo: 1-2 archivos

  • Tests rápidos: mocks en memoria

  • Mantenible: cambios localizados

Conclusiones

Diseñar pruebas de integración escalables con mocks no es solo cuestión de conocer las herramientas, sino de aplicar principios arquitectónicos sólidos desde el inicio. Los mocks son herramientas poderosas cuando se usan correctamente, pero pueden convertirse en una fuente de frustración si se aplican indiscriminadamente.

La clave está en encontrar el balance: usar mocks para aislar dependencias externas y casos extremos, pero mantener suficientes pruebas de integración reales para garantizar que los componentes funcionen correctamente cuando se conectan de verdad.

Recuerda que las pruebas son código de producción. Merecen el mismo cuidado en diseño, documentación y refactorización que el resto de tu aplicación. Una suite de pruebas bien diseñada no solo captura bugs, sino que documenta el comportamiento esperado y facilita cambios futuros con confianza.