8
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 5 years have passed since last update.

TISAdvent Calendar 2019

Day 17

ArchUnitを利用して、アーキテクチャのユニットテストを行う

Last updated at Posted at 2019-12-16

概要

ArchUnitなるライブラリを使用することで、Javaソースコードがアーキテクチャ違反を犯していないかを、ユニットテストで確認することが可能となります。

ArchUnitでできること、実際にどんなコードになるのか等紹介します。

ArchUnitについて

ArchUnitは、Javaアーキテクチャに関するユニットテストの実行を支援する オープンソースライブラリ です。
TECHNOLOGY RADARにも掲載された注目ライブラリです。

ArchUnitでできること

ArchUnitを使うと、以下のようなことをユニットテストで確認することができるようになります。()

  • パッケージ間の依存関係のチェック
  • クラス間の依存関係のチェック
  • パッケージ、クラス間の包含関係のチェック
  • 実装(implements)、継承関係のチェック
  • アノテーションのチェック
    • ex)EntityManagerを継承したクラスは、@Transactionalが付与されたクラスからのみアクセスされる、みたいなことをチェックできます
  • レイヤーのチェック
    • ex)Controller層,Service層,Persistence層の3レイヤがあるときに、Controller → Service → Persistence以外のアクセスは許さない、みたいなことをチェックできます
  • 循環参照のチェック

ArchUnitを使うと何が嬉しいのか

アーキテクチャ違反が起こりにくくなります。

従来、アーキテクチャをきれいな状態に保つために、

  • コーディングガイドを用意する
  • 上位者がコードレビューを行う

といったアナログな対策を行うことが多かったと思います。
そのため、何らかのヒューマンエラーが発生すると、アーキテクチャ違反したコードがmasterブランチに入ってしまいます。

ArchUnitを使用した場合、ユニットテストでアーキテクチャ違反を検出できるので、CIにてアーキテクチャ違反が起きていないかチェックすることができます。
よって、正しくテストが書けているならば、マージされるコードがアーキテクチャ違反を起こすことはありえません。

ArchUnitを使ったテスト

導入方法は ここ に記述されています。
以下で示したコードは こちら にあります。(JUnit5でテストしています)

ArchUnitを使ったテストを書くに当たって、まず、テストクラスに@AnalyzeClassesを付与します。
packagesで指定したpackageに含まれるクラスがテスト対象となります。

@AnalyzeClasses(packages = "nannany.arch.check.archunit")
public class ArchitectureTest {

以下は、package名にserviceが入っているpackageからは、package名にcontrollerが入っているpackageに依存することはできない、というアーキテクチャをチェックしています。

/**
 * serviceパッケージ配下はcontrollerに依存しない
 */
@ArchTest
public static final ArchRule servicesCantDependOnController = noClasses()
        .that().resideInAnyPackage("..service..")
        .should().dependOnClassesThat().resideInAPackage("..controller..");

記述方法は、should()の左側にテスト対象となるクラスを、should()の右側にルールを書いていくイメージです。
上記の例だと、noClasses().that().resideInAnyPackage("..service..") については、serviceと名のつくpackageに含まれるクラスにはshould()右側のルールに適合するクラスはない、ことを表します。
dependOnClassesThat().resideInAPackage("..controller.."); については、controllerと名のつくpackageに依存する、というるルールを表現しています。


ArchUnitでは典型的なアーキテクチャに対するサポートを行っています。
現状では、 多層アーキテクチャ オニオンアーキテクチャ(ヘキサゴナルアーキテクチャ) に対するサポートがあるようです。

以下では、多層アーキテクチャをサポートする用途のメソッドを使用して、各層間のアクセスについて、Controller → Service → Persistence以外のものがないかチェックしています。

/**
 * layeredArchitectureを使用した
 */
@ArchTest
public static final ArchRule layeredArchitecture = layeredArchitecture()
        .layer("Controllers").definedBy("nannany.arch.check.archunit.controller")
        .layer("Services").definedBy("nannany.arch.check.archunit.service")
        .layer("Repositories").definedBy("nannany.arch.check.archunit.repository")

        .whereLayer("Controllers").mayNotBeAccessedByAnyLayer()
        .whereLayer("Services").mayOnlyBeAccessedByLayers("Controllers")
        .whereLayer("Repositories").mayOnlyBeAccessedByLayers("Services");

参考

https://www.archunit.org/
https://www.thoughtworks.com/radar/tools/archunit

8
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
8
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?