美文网首页
ArchUnit:轻松测试软件架构

ArchUnit:轻松测试软件架构

作者: ReLive27 | 来源:发表于2023-02-21 21:25 被阅读0次

    为什么要测试你的架构?

    当项目变得更大,架构变得更加复杂。每个项目都有开发人员需要遵循的标准规则。
    新开发人员加入,他们可能会在不知情的情况下违反架构约束。如果每个人都在他们认为合适的地方添加新代码,每一个变化都可能对任何其他组件产生不可预见的影响,代码库就会变得混乱。

    当然,您可以让一名或多名经验丰富的开发人员担任架构师的角色,他们每周查看一次代码,找出违规行为并加以纠正。

    问题是这需要人工干预,有时我们并不能发现所有问题。保护软件架构免遭破坏的最佳方式是采用自动化流程。

    在本文中,我将展示解决此类问题的 ArchUnit 框架。您将看到典型的实际示例,以了解如何将此工具集成到您的项目中。

    什么是 ArchUnit

    ArchUnit 用于单元测试 Java 项目架构。它可以检查包和类、层和切片之间的依赖关系,检查循环依赖关系等等。通常,开发人员会建立通用模式。
    当有人违反规则时,测试将失败。开发人员将看到有关该问题的信息。它确保代码库保持完整,并且每个人都遵循准则。

    ArchUnit 演示

    我们创建一个 Spring Boot 项目,将 ArchUnit maven 依赖项添加到您的pom.xml:

    <dependency>
        <groupId>com.tngtech.archunit</groupId>
        <artifactId>archunit</artifactId>
        <version>1.0.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.tngtech.archunit</groupId>
        <artifactId>archunit-junit5</artifactId>
        <version>1.0.1</version>
        <scope>test</scope>
    </dependency>
    

    我们创建一个 ArchUnitTest.java 的Java类,我们将把所有的测试都放在那里。

    命名检查测试

    如果您有多个模块,您可以使用注释 @AnalyzeClasses 指出要扫描的包:

    @AnalyzeClasses(packages = "com.relive")
    

    例如,您可能希望 SpringBoot 项目中 Application 类名称应为“SpringBootTestApplication”:

    @ArchTest
        public static final ArchRule application_class_name_should_be =
                classes().that().areAnnotatedWith(SpringBootApplication.class)
                        .should().haveSimpleName("SpringBootTestApplication");
    
    

    您可以检查是否所有 Controller 类都具有后缀“Controller”:

    @ArchTest
        static ArchRule controllers_suffixed_should_be =
                classes().that().resideInAPackage("..controller..")
                        .or().areAnnotatedWith(RestController.class)
                        .should().haveSimpleNameEndingWith("Controller")
                        .allowEmptyShould(true);
    

    包位置测试

    您可能想检查实体类是否位于“entity”包中:

    @ArchTest
        static final ArchRule tablename_must_reside_in_a_entity_package =
                classes().that().areAnnotatedWith(TableName.class)
                        .should().resideInAPackage("..entity..")
                        .as("TableName should reside in a package '..entity..'")
                        .allowEmptyShould(true);
    

    同样,您可以检查配置类是否位于“config”包中:

    @ArchTest
        static final ArchRule configs_must_reside_in_a_config_package =
                classes().that().areAnnotatedWith(Configuration.class)
                        .or().areNotNestedClasses()
                        .and().areAnnotatedWith(ConfigurationProperties.class)
                        .should().resideInAPackage("..config..")
                        .as("Configs should reside in a package '..config..'")
                        .allowEmptyShould(true);
    
    

    注释测试

    所有配置类都应具有 @Configuration 或者 @ConfigurationProperties 其中之一:

        @ArchTest
        static ArchRule configs_should_be_annotated =
                classes()
                        .that().resideInAPackage("..config..")
                        .and().areNotNestedClasses()
                        .should().beAnnotatedWith(Configuration.class)
                        .orShould().beAnnotatedWith(ConfigurationProperties.class);
    

    图层测试

    所有 Service 层的 Java 类仅可以被 Controller 层访问,Dao 层的 Java 类仅可以被 Service 层访问:

    @ArchTest
        static ArchRule layer_inspection = layeredArchitecture()
                .consideringAllDependencies()
                .layer("Controller").definedBy("..controller..")
                .layer("Service").definedBy("..service..")
                .layer("Dao").definedBy("..dao..")
    
                .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
                .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
                .whereLayer("Dao").mayOnlyBeAccessedByLayers("Service");
    
    

    测试排除

    有时某些类我们不想执行规则,例如 JUnit 测试类。

    这可以通过以下方式轻松实现:

    @AnalyzeClasses(packages = "com.relive", importOptions = {ImportOption.DoNotIncludeTests.class})
    

    或者有一个你想忽略的类,因为规则不适用于它。我们可以创建一个自定义规则并导入它,如下所示:

    @AnalyzeClasses(packages = "com.relive", importOptions = {ArchUnitTest.ExcludeControllerImportOption.class})
    public class ArchUnitTest {
    
    
        static class ExcludeControllerImportOption implements ImportOption {
            @Override
            public boolean includes(Location location) {
                return !location.contains("SomeExcludedControllerClasses");
            }
        }
    
    }
    

    结论

    现在你已经了解如何将 ArchUnit 测试框架集成到您的 Java 项目中。您还熟悉了一些应用中的常用规则。

    如果你想了解更多关于 ArchUnit 的规则示例,你可以参考 ArchUnit 用户指南
    我想这里可以让你更加深入研究。

    与往常一样,本文中使用的源代码可在 GitHub 上获得。

    相关文章

      网友评论

          本文标题:ArchUnit:轻松测试软件架构

          本文链接:https://www.haomeiwen.com/subject/xyeokdtx.html