4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

アーキテクチャテストAdvent Calendar 2020

Day 1

ArchUnit 実践:Layered Architecture のアーキテクチャテスト

Last updated at Posted at 2020-12-01
// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* JUnit 5.7.0
* ArchUnit 0.14.1

レイヤーの依存関係

image.png

Java プロジェクトのパッケージ構成

image.png

アーキテクチャテストの実装

package com.example;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;

import static com.tngtech.archunit.library.Architectures.layeredArchitecture;

class ArchitectureTest {

    // 検査対象のクラス
    private static final JavaClasses CLASSES =
            new ClassFileImporter()
                    .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                    .importPackages("com.example");

    @Test
    void レイヤードアーキテクチャのアーキテクチャテスト() {
        layeredArchitecture()
            // presentation パッケージを UI 層として定義
            .layer("ui").definedBy("com.example.presentation..")
            // application パッケージをアプリケーション層として定義
            .layer("app").definedBy("com.example.application..")
            // domain パッケージをドメイン層として定義
            .layer("domain").definedBy("com.example.domain..")
            // infrastructure パッケージをインフラストラクチャ層として定義
            .layer("infra").definedBy("com.example.infrastructure..")

            // UI 層はどの層からも依存されない
            .whereLayer("ui").mayNotBeAccessedByAnyLayer()
            // アプリケーション層は UI 層からのみ依存される
            .whereLayer("app").mayOnlyBeAccessedByLayers("ui")
            // ドメイン層は UI 層、アプリケーション層からのみ依存される
            .whereLayer("domain").mayOnlyBeAccessedByLayers("ui", "app")
            // インフラストラクチャ層は UI 層、アプリケーション層、ドメイン層から依存される
            .whereLayer("infra").mayOnlyBeAccessedByLayers("ui", "app", "domain")

            .check(CLASSES);
    }
}

アーキテクチャテストの実行例(テスト失敗例)

アプリケーション層の Service クラスが、プレゼンテーション層の Helper クラスに依存してしまっている、というアーキテクチャ違反を検知した想定でのテスト失敗例。

$ ./gradlew clean check

> Task :test

ArchitectureTest > レイヤーアーキテクチャ() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'Layered architecture consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may not be accessed by any layer
    where layer 'app' may only be accessed by layers ['ui']
    where layer 'domain' may only be accessed by layers ['ui', 'app']
    where layer 'infra' may only be accessed by layers ['ui', 'app', 'domain']' was violated (2 times):
    Constructor <com.example.application.employee.EmployeeRegisterService.<init>(com.example.presentation.JsonRenderHelper)> has parameter of type <com.example.presentation.JsonRenderHelper> in (EmployeeRegisterService.java:0)
    Field <com.example.application.employee.EmployeeRegisterService.jsonRenderHelper> has type <com.example.presentation.JsonRenderHelper> in (EmployeeRegisterService.java:0)
        at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:94)
        at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:82)
        at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:267)
        at com.example.ArchitectureTest.レイヤーアーキテクチャ(ArchitectureTest.java:69)

1 test completed, 1 failed

> Task :test FAILED
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?