Archunit allows testing your “architecture” including class dependencies, cyclic dependencies, layer accesses, naming conventions, and inheritance checking.
1 2 3 4 <dependency > <groupId > com.tngtech.archunit</groupId > <artifactId > archunit</artifactId > </dependency >
Cyclic Dependency Test 1 2 3 4 5 6 7 @RunWith(ArchUnitRunner.class) @AnalyzeClasses(packages = "com.test") public class CyclicDependencyTest { @ArchTest public static final ArchRule rule = slices().matching("..services.(*).." ) .should().beFreeOfCycles(); }
Layer Access Test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RunWith(JUnit4.class) public class LayeredArchitectureTests { @Test public void layer_dependencies_are_respected () { JavaClasses importedClasses = new ClassFileImporter ().importPackages( "..com.test.." ); ArchRule myRule = layeredArchitecture() .layer("Controllers" ).definedBy("..com.test.controllers.." ) .layer("Services" ).definedBy("..com.test.services.." ) .layer("Infrastructure" ).definedBy("..com.test.infrastructure.." ) .whereLayer("Controllers" ).mayNotBeAccessedByAnyLayer() .whereLayer("Services" ).mayOnlyBeAccessedByLayers("Controllers" ) .whereLayer("Infrastructure" ).mayOnlyBeAccessedByLayers("Services" ); myRule.check(importedClasses); } }
Class location tests 1 2 3 4 5 6 7 8 @RunWith(ArchUnitRunner.class) @AnalyzeClasses(packages = "com.test") public class RepositoryPackageTest { @ArchTest public ArchRule repositories_should_located_in_infrastructure = classes() .that().areAnnotatedWith(Repository.class) .should().resideInAPackage("..infrastructure.." ); }
Method Return Type Test 1 2 3 4 5 6 7 8 9 10 @RunWith(ArchUnitRunner.class) @AnalyzeClasses(packages = "com.test.controllers") public class ControllerMethodReturnTypeTest { @ArchTest public ArchRule controller_public_methods_should_return = methods() .that().areDeclaredInClassesThat().resideInAPackage("..controllers.." ) .and().arePublic() .should().haveRawReturnType(BaseResponse.class) .because("here is the explanation" ); }
Naming Convention Test 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @RunWith(ArchUnitRunner.class) @AnalyzeClasses(packages = "com.test.controllers") public class NamingConventionTests { @ArchTest ArchRule controllers_should_be_suffixed = classes() .that().resideInAPackage("..controllers.." ) .should().haveSimpleNameEndingWith("Controller" ); } @RunWith(JUnit4.class) public class NamingConventionTests { @Test public void controllers_should_be_suffixed () { JavaClasses importedClasses = new ClassFileImporter () .importPackages("com.test.controllers" ); ArchRule rule = classes() .that().resideInAPackage("..controllers.." ) .should().haveSimpleNameEndingWith("Controller" ); rule.check(importedClasses); } }