La relación entre acoplamiento, cohesión y calidad de las pruebas unitarias es directa: componentes con dependencias controladas y responsabilidades claras se prueban con facilidad, se reutilizan en distintos contextos y acompaƱan la evolución del sistema sin fricción. Este tema analiza las ventajas concretas que surgen al mantener un bajo acoplamiento y una alta cohesión.
Las pruebas unitarias hacen visibles las decisiones de diseño. Si un caso de prueba requiere construir grafos de objetos complejos o inicializar recursos externos, probablemente el módulo tenga un acoplamiento innecesario o responsabilidades mezcladas. En cambio, cuando la clase es cohesiva, los tests se limitan a preparar datos representativos y verificar resultados en pocos pasos.
Reducir dependencias directas permite reemplazar colaboradores por dobles de prueba. Así, la ejecución de las pruebas no necesita infraestructura real ni configuraciones costosas.
La cohesión facilita que cada caso de prueba cubra un comportamiento específico. Si una clase narra una sola historia, el equipo puede enfocarse en validar condiciones normales, bordes y errores sin perderse en detalles irrelevantes. Además, la documentación necesaria disminuye porque el propio código explica su intención.
El siguiente ejemplo muestra un servicio de suscripciones cohesivo, equipado con dependencias inyectadas que pueden sustituirse durante los tests:
class ServicioSuscripcion {
private final RepositorioSuscripciones repositorio;
private final ValidadorSuscripcion validador;
private final ServicioNotificacion notificacion;
ServicioSuscripcion(RepositorioSuscripciones repositorio,
ValidadorSuscripcion validador,
ServicioNotificacion notificacion) {
this.repositorio = repositorio;
this.validador = validador;
this.notificacion = notificacion;
}
Suscripcion crear(PeticionSuscripcion peticion) {
validador.verificar(peticion);
Suscripcion suscripcion = Suscripcion.nueva(peticion);
repositorio.guardar(suscripcion);
notificacion.enviarConfirmacion(suscripcion);
return suscripcion;
}
}
El servicio solo coordina tres responsabilidades claras: validar, persistir y notificar. Cada colaborador insiste en una responsabilidad propia, lo que permite reemplazarlos por dobles de prueba durante los tests.
Al testear el servicio anterior con JUnit se obtiene una configuración breve:
@ExtendWith(MockitoExtension.class)
class ServicioSuscripcionTest {
@Mock RepositorioSuscripciones repositorio;
@Mock ValidadorSuscripcion validador;
@Mock ServicioNotificacion notificacion;
@InjectMocks ServicioSuscripcion servicio;
@Test
void creaSuscripcionValida() {
PeticionSuscripcion peticion = nuevaPeticion();
Suscripcion esperada = Suscripcion.nueva(peticion);
when(repositorio.guardar(any())).thenReturn(esperada);
Suscripcion resultado = servicio.crear(peticion);
assertEquals(esperada, resultado);
verify(validador).verificar(peticion);
verify(notificacion).enviarConfirmacion(esperada);
}
}
El uso de anotaciones de Mockito permite concentrarse en la lógica y no en la infraestructura. El test es legible porque la clase bajo prueba es cohesiva y solo requiere dependencias esenciales.
El mismo diseño favorece la modularidad. Componentes poco acoplados se pueden mover de un proyecto a otro sin reescrituras invasivas. Además, al estar altamente cohesionados, su intención se mantiene intacta y los equipos entienden rápido cómo integrarlos.
Un portafolio de pruebas que cubre módulos bien diseñados actúa como red de seguridad. Los cambios guiados por nuevas reglas de negocio pueden introducirse sin temor, porque los tests fallarán si el diseño pierde cohesión o las dependencias se vuelven estrechas. Esto habilita ciclos de entrega continua y reduce la deuda técnica.
Para sostener en el tiempo estos beneficios conviene:
El equilibrio entre acoplamiento y cohesión no solo embellece la arquitectura; también multiplica la efectividad de las pruebas unitarias y la modularidad. Al invertir en diseño limpio, el equipo garantiza que cada iteración llegue con confianza a producción.