// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* JUnit 5.7.0
* ArchUnit 0.14.1
アーキテクチャテストのモチベーション
タイムゾーンを考慮する必要があるアプリケーション1で、以下の実装ルールに準拠していることを担保したい。
- 原則として
ZonedDateTime
,OffsetDateTime
の使用を強制したい - ただし、実装の都合で LocalDateTime インスタンスを扱いたい場合があり、LocalDateTime 型への依存自体は許容したい
- LocalDateTime インスタンスをアプリコード内で生成する必要はなく、LocalDateTime インスタンスの生成方法のうち、最低限、
LocalDateTime.now()
,LocalDateTime.of(...)
,LocalDateTime.parse(...)
が使用されないことを担保したい
アーキテクチャテストの実装
package com.example;
import com.tngtech.archunit.core.domain.AccessTarget.MethodCallTarget;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ConditionEvents;
import com.tngtech.archunit.lang.SimpleConditionEvent;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
class ArchitectureTest {
// 検査対象のクラス
private static final JavaClasses CLASSES =
new ClassFileImporter()
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
.importPackages("com.example");
@Test
void doNotInstantiateLocalDateTime() {
methods().should(new ArchCondition<>("not instantiate LocalDateTime") {
@Override
public void check(final JavaMethod method, final ConditionEvents events) {
method.getMethodCallsFromSelf().forEach(javaMethodCall -> {
// 呼び出したメソッド
MethodCallTarget calledMethod = javaMethodCall.getTarget();
// 呼び出したメソッドが定義されたクラス
JavaClass calledClass = calledMethod.getOwner();
// LocalDateTime の生成か
boolean isLocalDateTimeInstantiated =
calledClass.isAssignableTo(LocalDateTime.class)
&& (
calledMethod.getName().equals("now")
|| calledMethod.getName().equals("of")
|| calledMethod.getName().equals("parse")
);
if (isLocalDateTimeInstantiated) {
// 実装ルール違反を通知
events.add(SimpleConditionEvent.violated(
method,
String.format("`%s` calls `%s`.", method.getFullName(), calledMethod.getFullName()))
);
}
});
}
})
.check(CLASSES);
}
}
アーキテクチャテストの失敗例
ArchitectureTest > doNotInstantiateLocalDateTime() FAILED
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'methods should not instantiate LocalDateTime' was violated (4 times):
`com.example.domain.DomainService.hoge1()` calls `java.time.LocalDateTime.now()`.
`com.example.domain.DomainService.hoge2()` calls `java.time.LocalDateTime.of(java.time.LocalDate, java.time.LocalTime)`.
`com.example.domain.DomainService.hoge3()` calls `java.time.LocalDateTime.of(int, int, int, int, int, int)`.
`com.example.domain.DomainService.hoge4()` calls `java.time.LocalDateTime.parse(java.lang.CharSequence)`.