====== 06 - Pruebas de arquitectura: ArchUnit ====== Una **prueba de arquitectura** comprueba que el código respeta las reglas estructurales acordadas: qué capa puede depender de cuál, dónde están las clases, si hay ciclos de dependencia. **ArchUnit** analiza el bytecode Java y convierte esas reglas en pruebas que fallan cuando el código las rompe. ===== 1. Por qué probar la arquitectura ===== El diagrama de clases del tema anterior muestra las dependencias; las pruebas de arquitectura las **imponen automáticamente**. Sin ellas, las reglas se cumplen mientras alguien las recuerda; con ellas, se comprueban en cada ''mvn test''. @AnalyzeClasses(packages = "com.example.library") class ArchitectureTest { @ArchTest void controllersDoNotDependOnPersistence() { noClasses().that().resideInAPackage("..controller..") .should().dependOnClassesThat() .resideInAPackage("..persistence..") .check(); } } La anotación ''@AnalyzeClasses'' indica qué paquetes analizar y ''check()'' ejecuta la regla sobre las clases importadas. ===== 2. Reglas de capas ===== ArchUnit describe la estructura por capas y sus dependencias permitidas: @ArchTest void respectsLayeredArchitecture() { LayeredArchitecture layers = layeredArchitecture().consideringAllDependencies() .layer("Controller").definedBy("..controller..") .layer("Service").definedBy("..service..") .layer("Persistence").definedBy("..persistence.."); layers.whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service") .checkClasses(); } ^ Regla ^ Significado ^ | ''mayOnlyBeAccessedByLayers'' | Qué capas pueden usar esta capa | | ''mayNotBeAccessedByAnyLayer'' | Nadie la usa desde fuera; solo se accede hacia dentro | ===== 3. Otras reglas útiles ===== // Los servicios terminan en Service classes().that().resideInAPackage("..service..") .should().haveSimpleNameEndingWith("Service") .check(); // Sin ciclos entre paquetes slices().matching("com.example.(**)").should().beFreeOfCyclicDependencies() .check(); // El dominio no depende del framework noClasses().that().resideInAPackage("..domain..") .should().dependOnClassesThat() .resideInAnyPackage("org.springframework..") .check(); ^ Tipo de regla ^ Qué controla ^ | Dependencias entre paquetes | Límites de capa | | Ciclos | Que el grafo de dependencias sea acíclico | | Convenciones de nombres | Dónde vive cada tipo de clase | ===== 4. Conexión con los diagramas ===== Las reglas de ArchUnit son la versión ejecutable de lo que se dibuja: * El **diagrama de clases** documenta las dependencias diseñadas. * El diagrama **C4 de componentes** muestra los módulos permitidos. * **ArchUnit** comprueba que el código real se ajusta a ambos. Las reglas de arquitectura no se negocian prueba a prueba: se acuerdan y se automatizan. Si una regla debe cambiar, se cambia la regla deliberadamente, no rompiéndola en silencio.