by Emre Savc?
由EmreSavc?
如何使用ArchUnit測試Java項目的體系結構 (How to test your Java project’s architecture with ArchUnit)
In this post, I will show you an interesting library called ArchUnit that I met recently. It does not test your code flow or business logic. The library lets you test your “architecture” including class dependencies, cyclic dependencies, layer accesses, naming conventions, and inheritance checking.
在這篇文章中,我將向您展示我最近遇到的一個有趣的庫ArchUnit。 它不會測試您的代碼流或業務邏輯。 該庫使您可以測試“體系結構”,包括類依賴關系,循環依賴關系,圖層訪問,命名約定和繼承檢查。
Here is the list of tests which we will write in this post:
這是我們將在本文中編寫的測試列表:
- Cyclic Dependency Test 循環依賴測試
- Layer Access Test 層訪問測試
- Class Location Test 課堂位置測試
- Method Return Type Test 方法返回類型測試
- Naming Convention Test 命名約定測試
So let's imagine a project with the package structure shown below:
因此,讓我們想象一個具有如下所示的包結構的項目:
Before writing tests for our architecture, as a start point, we decide that our controllers should not be accessed from any other class or package. Also conceptually we accept that controller names should end with the “…Controller” suffix.
首先,為我們的體系結構編寫測試之前,我們決定不應從任何其他類或程序包訪問控制器。 同樣從概念上講,我們接受控制器名稱應以“ ... Controller”后綴結尾。
Now it is time to get our hands dirty. Below, we start writing our first test. It allows us to check our naming convention.
現在是時候弄臟我們的手了。 下面,我們開始編寫我們的第一個測試。 它使我們可以檢查我們的命名約定。
命名約定測試 (Naming Convention Tests)
@RunWith(ArchUnitRunner.class)@AnalyzeClasses(packages = "com.test.controllers")public class NamingConventionTests { @ArchTest ArchRule controllers_should_be_suffixed = classes() .that().resideInAPackage("..controllers..") .should().haveSimpleNameEndingWith("Controller"); }
When we run the test we see that it passes:
當我們運行測試時,我們看到它通過了:
There are two types of tests with arc unit. One of them is like the one shown above. If we want, we can write tests using JUnit's Test
annotation. Change the RunWith
parameter to JUnit4.class
and remove the AnalyzeClasses
annotation.
電弧單元有兩種測試類型。 其中之一就像上面顯示的那樣。 如果需要,可以使用JUnit的Test
批注編寫測試。 將RunWith
參數更改為JUnit4.class
并刪除AnalyzeClasses
批注。
In this way, we specify the packages to import using ClassFileImporter
within ArcUnit.
通過這種方式,我們在ArcUnit中指定使用ClassFileImporter
導入的包。
@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); }}
Now, let's see what happens if we have a different suffix. Change ("Controller") to ("Ctrl")
and run:
現在,讓我們看看如果使用不同的后綴會發生什么。 將("Controller") to ("Ctrl")
更改("Controller") to ("Ctrl")
并運行:
The exception says that: “java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] — Rule ‘classes that reside in a package ‘..controllers..’ should have a simple name ending with ‘Ctrl’’ was violated (1 time):the simple name of com.test.controllers.FirstController does not end with ‘Ctrl’ in (FirstController.java:0)”
異常表示:“ java.lang。 AssertionError : 違反體系結構 [優先級:中]- 違反了規則'類,位于包'..controllers ..'中,其簡單名稱以'Ctrl'結尾(1次):com.test的簡單名稱.controllers。 FirstController在(FirstController.java:0) 中不以'Ctrl'結尾 ”
So far so good. We wrote our first test and it correctly runs. Now it’s time to jump to other tests.
到目前為止,一切都很好。 我們編寫了第一個測試,它可以正確運行。 現在該跳到其他測試了。
課堂位置測試 (Class Location Tests)
Let's write another rule that makes sure that classes which have annotation repositories should be located in the infrastructure package.
讓我們編寫另一條規則,以確保具有批注存儲庫的類應位于基礎結構包中。
@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..");}
If we annotate other classes than infrastructure packages, the test raises an AssertionError.
如果我們注釋除基礎結構軟件包以外的其他類,則測試將引發AssertionError。
方法返回類型測試 (Method Return Type Tests)
Let's write some method checks. Suppose we decide that our controller methods should return a type BaseResponse.
讓我們編寫一些方法檢查。 假設我們決定控制器方法應返回BaseResponse類型。
@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");}
循環依賴測試 (Cyclic Dependency Tests)
In this day and age, cyclic dependency issues are handled by most of the IOC containers. It is a good thing to have some tool that tests it for us.
在當今時代,大多數IOC容器都處理周期性依賴問題。 擁有一些可以為我們測試它的工具是一件好事。
Now first create classes that have cyclic complexity:
現在,首先創建具有循環復雜性的類:
package com.test.services.slice1;import com.test.services.slice2.SecondService;public class FirstService { private SecondService secondService; public FirstService() { this.secondService = new SecondService(); }}
package com.test.services.slice2;import com.test.services.slice1.FirstService;public class SecondService { private FirstService firstService; public SecondService() { this.firstService = new FirstService(); }}
FirstService and SecondService depend on each other, and it creates the cycle.
FirstService和SecondService相互依賴,并創建周期。
Now write a test for it:
現在為它編寫一個測試:
@RunWith(ArchUnitRunner.class)@AnalyzeClasses(packages = "com.test")public class CyclicDependencyTest { @ArchTest public static final ArchRule rule = slices().matching("..services.(*)..") .should().beFreeOfCycles();}
Running this test gives the below result:
運行此測試將得出以下結果:
Also, the result is the same as constructor injection.
而且,結果與構造函數注入相同。
層測試 (Layer Tests)
Now it is time to write a layer test which covers our layers.
現在是時候編寫覆蓋我們各層的層測試了。
@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); }}
We make a violation of the above rules to see that our test fails — we inject a service into the repository.
我們違反了以上規則,看到我們的測試失敗了—我們將服務注入到存儲庫中。
package com.test.infrastructure;import com.test.services.SecondService;public class FirstRepository { SecondService secondService; public FirstRepository(SecondService secondService) { this.secondService = secondService; }}
When we run the test we will see that our repository violates the rules:
運行測試時,我們將看到我們的存儲庫違反了規則:
結語 (Wrapping up)
ArchUnit, as you see, ensures that your project has the right architecture. It helps you to keep the project structure clean and prevents developers from making breaking changes.
如您所見,ArchUnit確保您的項目具有正確的體系結構。 它可以幫助您保持項目結構的清潔,并防止開發人員進行重大更改。
We have done a brief overview of the library. Besides all of its features, I think it would be great if ArchUnit would have some rules to test hexagonal architecture, cqrs and some DDD concepts like aggregates, value objects, etc.
我們已經對該庫進行了簡要概述。 除了所有功能之外,我認為ArchUnit擁有一些規則來測試六角形體系結構 , cqrs和一些DDD概念(如聚合,值對象等)將是很棒的。
For curious ones, here is the code on Github:
對于好奇的人,這里是Github上的代碼:
mstrYoda/java-archunit-examplesContribute to mstrYoda/java-archunit-examples development by creating an account on GitHub.github.comTNG/ArchUnitA Java architecture test library, to specify and assert architecture rules in plain Java - TNG/ArchUnitgithub.comUnit test your Java architectureStart enforcing your architecture within 30 minutes using the test setup you already have.www.archunit.org
mstrYoda / java-archunit-examples 通過在GitHub上創建一個帳戶為mstrYoda / java-archunit-examples開發做出貢獻。 github.com TNG / ArchUnit 一個Java架構測試庫,用于以純Java指定和聲明架構規則 -TNG / ArchUnit github.com 對Java架構進行單元測試 開始使用已有的測試設置在30分鐘內開始實施架構。 www.archunit.org
翻譯自: https://www.freecodecamp.org/news/java-archunit-testing-the-architecture-a09f089585be/