Spring Boot-使用ArchUnit对项目架构进行单元测试


在构建软件时,开发团队通常会定义一组被视为最佳实践的准则和代码约定。

这些实践通常记录在案,并传达给已接受这些实践的整个开发团队。但是,在开发过程中,开发人员可能会违反这些准则,这些准则是在代码检查或使用代码质量检查工具时发现的。

因此,重要的方面是在整个项目体系结构中使这些指令尽可能自动化,以优化审查。

我们可以使用ArchUnit将这些准则强加为可验证的JUnit测试。如果引入了体系结构违规,它可以保证将停止提供软件版本。

ArchUnit 是一个免费,简单且可扩展的库,可使用任何简单的Java单元测试框架来检查Java代码的体系结构。也就是说,ArchUnit可以检查包与类,层和切片之间的依赖关系,检查循环依赖关系等等。通过分析给定的Java字节码,将所有类导入Java代码结构来完成此操作。

通过ArchUnit,您可以通过可执行测试的形式来实现应用程序体系结构的静态属性的规则,例如:

  • 软件包依赖关系检查
  • 类依赖检查
  • 类和包的包装检查
  • 继承检查
  • 注释检查
  • 图层检查
  • 周期检查

Getting Started ArchUnit的JUnit 5支持只需在Maven Central中添加以下依赖项:

pom.xml

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

build.gradle

dependencies { 
  testImplementation 'com.tngtech.archunit:archunit-junit5:0.14.1' 
} 


Package Dependency Checks

class ArchunitApplicationTests { private JavaClasses importedClasses; @BeforeEach public void setup() { importedClasses = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("com.springboot.testing.archunit"); } @Test void servicesAndRepositoriesShouldNotDependOnWebLayer() { noClasses() .that().resideInAnyPackage("com.springboot.testing.archunit.service..") .or().resideInAnyPackage("com.springboot.testing.archunit.repository..") .should() .dependOnClassesThat() .resideInAnyPackage("com.springboot.testing.archunit.controller..") .because("Services and repositories should not depend on web layer") .check(importedClasses); } }

服务和存储库不应与Web层通信。

**Class Dependency Checks**

class ArchunitApplicationTests { private JavaClasses importedClasses; @BeforeEach public void setup() { importedClasses = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("com.springboot.testing.archunit"); } @Test void serviceClassesShouldOnlyBeAccessedByController() { classes() .that().resideInAPackage("..service..") .should().onlyBeAccessed().byAnyPackage("..service..", "..controller..") .check(importedClasses); } }

ArchUnit提供了一个类似于DSL的抽象fluent API,可以反过来针对导入的类进行评估。服务只能由Controller访问。

这两个点代表任意数量的程序包(比较AspectJ Pointcuts)。 

**Naming convention**

class ArchunitApplicationTests { private JavaClasses importedClasses; @BeforeEach public void setup() { importedClasses = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("com.springboot.testing.archunit"); } @Test void serviceClassesShouldBeNamedXServiceOrXComponentOrXServiceImpl() { classes() .that().resideInAPackage("..service..") .should().haveSimpleNameEndingWith("Service") .orShould().haveSimpleNameEndingWith("ServiceImpl") .orShould().haveSimpleNameEndingWith("Component") .check(importedClasses); } @Test void repositoryClassesShouldBeNamedXRepository() { classes() .that().resideInAPackage("..repository..") .should().haveSimpleNameEndingWith("Repository") .check(importedClasses); } @Test void controllerClassesShouldBeNamedXController() { classes() .that().resideInAPackage("..controller..") .should().haveSimpleNameEndingWith("Controller") .check(importedClasses); }

}

通用规则是命名约定。例如,所有服务类名称都必须以Service,Component等结尾。

class ArchunitApplicationTests { private JavaClasses importedClasses; @BeforeEach public void setup() { importedClasses = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("com.springboot.testing.archunit"); } @Test void fieldInjectionNotUseAutowiredAnnotation() { noFields() .should().beAnnotatedWith(Autowired.class) .check(importedClasses); } @Test void repositoryClassesShouldHaveSpringRepositoryAnnotation() { classes() .that().resideInAPackage("..repository..") .should().beAnnotatedWith(Repository.class) .check(importedClasses); } @Test void serviceClassesShouldHaveSpringServiceAnnotation() { classes() .that().resideInAPackage("..service..") .should().beAnnotatedWith(Service.class) .check(importedClasses); } }

ArchUnit Lang API可以为Java类的成员定义规则。例如,如果在特定上下文中的方法需要使用特定的注释进行注释,或者如果返回类型实现特定的接口,则这可能是相关的。

**Layer Checks**

class ArchunitApplicationTests { private JavaClasses importedClasses; @BeforeEach public void setup() { importedClasses = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("com.springboot.testing.archunit"); } @Test void layeredArchitectureShouldBeRespected() { layeredArchitecture() .layer("Controller").definedBy("..controller..") .layer("Service").definedBy("..service..") .layer("Repository").definedBy("..repository..") .whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service") .check(importedClasses); } } ```

在Spring Boot应用中,服务层取决于存储库层,控制器层取决于服务层。

ArchUnit提供了一组功能来断言您的分层体系结构受到尊重。这些测试可自动保证访问和使用保持在您定义的范围内。因此可以编写自定义规则。在本文中,我们仅描述了一些规则。该官员ArchUnit指南给出了不同的可能性。


原文链接:http://codingdict.com