Spring Boot Testing: guía completa de buenas prácticas
Introducción. En el desarrollo moderno, probar no es una actividad uniforme. Cada tipo de prueba cumple un propósito distinto, desde validar unidades de código en milisegundos hasta comprobar flujos completos del sistema en condiciones cercanas a producción. Diseñar una estrategia eficiente y mantenible exige comprender los tipos de pruebas, su alcance y sus compromisos en velocidad, fiabilidad y cobertura. La regla de oro del principio de la pirámide de pruebas es clara: muchas pruebas unitarias rápidas y baratas en la base, algunas de integración en el medio y pocas E2E y de rendimiento en la cima. Un ratio habitual: 70–80 por ciento unitarias, 15–20 por ciento de integración y 5–10 por ciento end to end.
Cuándo usar cada tipo. Unitarias para lógica de negocio, algoritmos y utilidades en aislamiento; integración para interacciones entre componentes, repositorios y servicios internos; de componente o slice cuando necesitas probar una capa o módulo con algunas dependencias reales sin levantar toda la app; E2E solo para recorridos de usuario críticos que cruzan múltiples sistemas; funcionales para verificar requisitos de negocio; de contrato en comunicaciones entre servicios o microservicios; de rendimiento para requisitos de latencia y carga; smoke para validar rápidamente un despliegue; regresión para evitar reintroducir errores; seguridad para detectar vulnerabilidades de forma temprana.
Pruebas unitarias. Propósito: verificar clases y métodos de forma aislada, sin base de datos, red ni sistema de archivos. Mejores prácticas: simula dependencias externas con mocks o stubs para ganar velocidad y estabilidad; usa el patrón AAA con secciones claras de preparación, ejecución y verificación; cubre bordes y casos de error además del camino feliz; nombra los tests de forma descriptiva usando el patrón shouldComportamientoWhenCondicion; mantén independencia entre pruebas sin estado compartido; valida un único comportamiento por prueba; utiliza constructores de datos de prueba para legibilidad. En JUnit 5 con Mockito, puedes crear mocks manualmente o usar anotaciones como Mock e InjectMocks, habilitadas con la extensión de Mockito. Verifica interacciones con verify, captura argumentos con ArgumentCaptor para afirmar propiedades de los objetos pasados, y usa thenAnswer para comportamientos dinámicos en función de la entrada. Para métodos void emplea doNothing, doThrow o doAnswer. El estilo BDD con BDDMockito separa claramente Given, When, Then y mejora la legibilidad. Ejecuta en paralelo cuando el diseño sea sin estado y no compartas recursos mutables. Controla el ciclo de vida con TestInstance PER_CLASS si necesitas optimizar inicializaciones costosas o compartir recursos entre métodos. Sigue los principios FIRST: rápidas, aisladas, repetibles, auto verificables y oportunas.
Pruebas de componente o slice. Propósito: probar una o varias piezas gestionadas por Spring sin arrancar toda la aplicación. Mejores prácticas: usa anotaciones de slice como WebMvcTest para controladores, DataJpaTest para repositorios, RestClientTest para clientes HTTP y JsonTest para serialización; maqueta colaboradores ajenos al slice con MockBean o Mock; valida manejo de errores, validaciones y mapeos JSON. Emplea perfiles de Spring para pruebas con un application test y propiedades específicas, activándolo con ActiveProfiles. Puedes definir beans solo de prueba con clases de configuración en el código de test o con TestConfiguration dentro del propio caso, e importarlas cuando proceda. Esto mantiene rápidos los tests y separa claramente la configuración de producción de la de pruebas.
Pruebas de integración. Propósito: asegurar que varios componentes colaboran correctamente y que el flujo de datos atraviesa controladores, servicios, repositorios y la base de datos real. Por qué importan: conectan las capas, descubren diferencias de entorno que H2 no revela frente a Postgres, y son guardianes en CI CD. Mejores prácticas: usa bases reales cuando sea posible, preferentemente con contenedores; aísla dependencias externas con dobles de prueba mientras mantienes reales las internas; usa transacciones con rollback para estado limpio; centra la cobertura en caminos de negocio críticos. Usa SpringBootTest con criterio porque levanta todo el contexto; prefiere slices cuando baste. Optimiza reutilizando el contexto de Spring entre clases y evita DirtiesContext salvo necesidad. Puedes probar entradas HTTP con MockMvc para velocidad o con TestRestTemplate para la pila completa; para llamadas salientes, emplea MockWebServer si buscas algo ligero o WireMock cuando necesites emparejar por URL, cabeceras o cuerpo, inyectar fallos, manejar escenarios y grabar respuestas. Valida seguridad con usuarios simulados y roles esperados.
Testcontainers. Qué es: arranca servicios reales efímeros con Docker durante las pruebas, como Postgres, Kafka, Redis o Mongo, y los detiene al finalizar. Por qué usarlo: reduce el desfase entre pruebas y producción respecto a in memory, otorgando confianza y realismo. Mejores prácticas: define contenedores estáticos por clase, inyecta propiedades con DynamicPropertySource y comparte contextos para acelerar suites. Inconvenientes: requiere Docker, consume más recursos y es más lento que mocks, pero ofrece el mejor equilibrio entre velocidad, realismo y mantenibilidad cuando dependes de servicios externos.
Pruebas end to end. Propósito: validar el sistema completo desde fuera, con base de datos, mensajería y APIs externas simuladas, y en algunos casos interfaz de usuario. Mejores prácticas: prueba recorridos de usuario esenciales con impacto de negocio y deja bordes a unitarias e integración; usa dependencias reales cuando sea viable, ejecuta en puertos aleatorios y aísla servicios de terceros con WireMock; maneja asincronía con esperas robustas y timeouts; automatiza en CI CD, asumiendo menor frecuencia por su coste. Un ejemplo típico: registro de usuario que persiste en la base real y llama a un servicio de correo simulado, verificando respuesta 201 y que la petición externa recibió el payload esperado. Alternativas al cliente HTTP: WebTestClient con API fluida, también válido en aplicaciones servlet, o RestAssured para un estilo cercano a BDD.
Pruebas de contrato. Propósito: asegurar compatibilidad entre productor y consumidor verificando que el proveedor cumple con lo que el consumidor espera y viceversa, sin depender de E2E pesadas entre muchos servicios. Beneficios: despliegues independientes, menos fragilidad y detección temprana de cambios rompientes. Mejores prácticas: usa contratos dirigidos por el consumidor CDC; adopta frameworks como Spring Cloud Contract o Pact; versiona contratos y almacénalos en control de versiones; automatiza la verificación cuando cambian productor o consumidor; valida esquemas y códigos de error. Flujo típico con Pact: el consumidor genera el contrato y lo publica en un broker durante su pipeline; el proveedor lo descarga y lo verifica contra su aplicación real en su pipeline; si las verificaciones pasan, el proveedor puede desplegar sin romper al consumidor.
Consejos prácticos transversales. Mantén pocos E2E estables y de alto valor; maximiza unitarias para iterar rápido; usa slices para validar capas con rapidez; reserva integración para rutas críticas; maqueta lo externo e integra lo interno; reutiliza contextos y recursos para acelerar; opta por Testcontainers cuando la fidelidad con producción marque la diferencia. Con esta estrategia escalas calidad, velocidad y fiabilidad.
Sobre Q2BSTUDIO. Somos una empresa de desarrollo de software que impulsa calidad desde el diseño: aplicaciones a medida, software a medida, servicios cloud aws y azure, ciberseguridad y pentesting, servicios inteligencia de negocio con power bi, así como inteligencia artificial e ia para empresas con agentes IA. Te ayudamos a definir una pirámide de pruebas efectiva, pipelines CI CD robustos y entornos de pruebas realistas con contenedores. Conoce cómo abordamos proyectos de desarrollo y modernización en nuestro servicio de aplicaciones a medida y cómo reforzamos la protección de tus productos con nuestra práctica de ciberseguridad. Juntos elevamos la calidad de tus entregables, optimizamos costes y reducimos riesgos en producción.