[JAVA] Première unité d'arche

Cette fois, j'aimerais écrire sur une bibliothèque appelée ArchUnit qui m'intéressait particulièrement dans Technology Radar Vol.19 annoncé en novembre 2018. pense.

objectif

introduction

Cet article montre le résultat du déplacement réel de la main tout en faisant référence aux informations officielles, mais si vous l'implémentez à l'aide d'ArchUnit, ce n'est pas cet article, mais le [Guide de l'utilisateur d'ArchUnit] officiel (https: //) Veuillez vous référer à www.archunit.org/userguide/html/000_Index.html). De plus, si vous trouvez quelque chose qui ne va pas dans l'article, veuillez commenter.

Qu'est-ce que ArchUnit?

ArchUnit est une bibliothèque de test d'architecture pour les applications écrites en Java / Kotlin. Étant donné qu'ArchUnit lui-même est OSS, le code source est publié sur GitHub. https://github.com/TNG/ArchUnit/blob/master/licenses/asm.license

ArchUnit vous permet d'effectuer divers tests architecturaux tels que les dépendances entre les packages et les classes, la vérification de l'empaquetage des classes, la vérification des références circulaires, etc. De plus, ArchUnit est une bibliothèque qui peut facilement implémenter la «fonction d'adaptation» décrite dans Evolutionary Architecture [Technology Radar]( Il est présenté sur https://www.thoughtworks.com/radar/tools/archunit). La fonction d'adaptabilité est un indice de mesure des caractéristiques d'une architecture. La fonction d'adaptabilité peut être réalisée en incorporant le test en utilisant ArchUnit dans CI / CD. Pour ceux qui souhaitent en savoir plus sur la fonction d'adaptabilité, veuillez consulter Evolutionary Architecture et [Technology Radar Vol.18](https: //www.thoughtworks. Voir com / radar / techniques / architecture-fitness-function).

Que pouvez-vous faire avec ArchUnit?

À partir de là, je voudrais présenter le test réel utilisant ArchUnit dans la base de code. Je me réfère au Guide de l'utilisateur ArchUnit, donc si vous voulez en savoir plus, assurez-vous de vous y référer. L'environnement d'exploitation est supposé être Maven 3.5.4, Spring Boot 2.1.0, Kotlin 1.3 et JUnit 4.

introduction

Pour JUnit4 / Maven, ajoutez ce qui suit à pom.xml.

pom.xml


<dependency>
    <groupId>com.tngtech.archunit</groupId>
    <artifactId>archunit-junit4</artifactId>
    <version>0.9.3</version>
    <scope>test</scope>
</dependency>

Lors de la création d'une classe de test, chargez ArchUnitRunner à l'aide de l'annotation @RunWith comme indiqué ci-dessous. Spécifiez également le package de base de l'application à tester avec l'annotation @AnalyzeClasses et ajoutez @ ArchTest à la méthode de test.

Sample.kt


@RunWith(ArchUnitRunner::class)
@AnalyzeClasses(packages = ["com.dais39.sample.app"])
class TestApplicationRules {

    @ArchTest
    fun test(classes: JavaClasses) {
        //Processus de test
    }
}

Test des dépendances entre les packages

Si vous souhaitez tester les dépendances des packages, écrivez un code comme celui-ci: Dans ArchUnit, en tant que flux de test de base, rule est décrit dans la chaîne de méthodes, et finalement le test est effectué en appelant check (). Une caractéristique d'ArchUnit est que vous pouvez écrire du code expressif en connectant des méthodes. L'exemple ci-dessous garantit que toutes les classes faisant référence à une classe placée dans le package d'application sont placées dans la présentation ou le package d'application. Le test échouera s'il est référencé par un autre package.

Sample.kt


@RunWith(ArchUnitRunner::class)
@AnalyzeClasses(packages = ["com.dais39.sample.app"])
class TestApplicationRules {

    @ArchTest
Les classes de la couche d'application amusante sont indépendantes des classes autres que la couche de présentation(classes: JavaClasses) {

        val rules = ArchRuleDefinition.classes().that().resideInAPackage("..application..")
            .should().onlyBeAccessed().byClassesThat().resideInAPackage("..presentation..")

        rules.check(classes)
    }
}

Test des dépendances entre les classes

Voici un test si vous souhaitez vous assurer que la classe Service n'est accessible qu'à partir de la classe Controller: Vous pouvez utiliser haveNameMatching () pour faire correspondre des modèles avec des expressions régulières, mais vous pouvez utiliser haveSimpleName () pour spécifier directement le nom de la classe. Il existe d'autres méthodes de correspondance de modèles.

Sample.kt


@ArchTest
La classe Fun Service n'est accessible qu'à partir de la classe Controller(classes: JavaClasses){
        
    val rules = ArchRuleDefinition.classes().that().haveNameMatching(".*Service")
            .should().onlyBeAccessed().byClassesThat().haveNameMatching(".*Controller")

    rules.check(classes)
}

Test de placement en classe

Un test qui garantit qu'une classe se trouve dans un package particulier s'écrit comme suit:

Sample.kt


@ArchTest
Les classes dont les noms commencent par Fun ToDoService sont placées dans le package d'application(classes: JavaClasses){

    val rules = ArchRuleDefinition.classes().that().haveSimpleNameStartingWith("ToDoService")
            .should().resideInAPackage("..application..")

    rules.check(classes)
}

Héritage, tests liés à la mise en œuvre

Un test qui garantit qu'une interface est implémentée uniquement à partir d'une classe avec certaines conditions s'écrit comme suit. Dans l'exemple ci-dessous, la condition est une classe qui se termine par la chaîne spécifiée à l'aide de haveSimpleNameEndingWith ().

Sample.kt


@ArchTest
fun ToDoServiceImpl implémente l'interface ToDoService(classes: JavaClasses){

        val rules = ArchRuleDefinition.classes().that().implement(ToDoService::class.java)
            .should().haveSimpleNameEndingWith("ToDoServiceImpl")

        rules.check(classes)
    }

Test des dépendances pour les classes annotées

Voici un test qui garantit que la classe ToDoService n'est accessible qu'à partir de la classe avec @ RestController.

Sample.kt


@ArchTest
La classe amusante ToDoService est uniquement accessible par la classe avec l'annotation RestController(classes: JavaClasses){

    val rules = ArchRuleDefinition.classes().that().areAssignableTo(ToDoService::class.java)
            .should().onlyBeAccessed().byClassesThat().areAnnotatedWith(RestController::class.java)

    rules.check(classes)
}

Tester la configuration du package en fonction de l'architecture de la couche

ArchUnit vous permet d'écrire des tests qui garantissent une configuration de package conforme à l'architecture de la couche. Actuellement, il ne prend en charge que l'architecture de couches, mais il semble qu'il supportera l'architecture hexagonale et l'architecture propre à l'avenir. Les tests suivants garantissent une architecture qui applique le DIP (Dependency Reversal Principle) à l'architecture de la couche.

Sample.kt


@ArchTest
La structure de l'application amusante suit l'architecture des couches(classes: JavaClasses){
    val rules = Architectures.layeredArchitecture()
            .layer("Presentation").definedBy("..presentation..")
            .layer("Application").definedBy("..application..")
            .layer("Domain").definedBy("..domain..")
            .layer("Infrastructure").definedBy("..infra..")

            .whereLayer("Presentation").mayNotBeAccessedByAnyLayer()
            .whereLayer("Application").mayOnlyBeAccessedByLayers("Presentation")
            .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure")
            .whereLayer("Infrastructure").mayNotBeAccessedByAnyLayer()

    rules.check(classes)
}

Test de référence circulaire

Si vous souhaitez créer un test qui vérifie la présence de références circulaires dans votre application, écrivez: Spécifiez le package cible avec matching ().

Sample.kt


@ArchTest
fun La référence circulaire n'existe pas(classes: JavaClasses){

    val rules  = SlicesRuleDefinition.slices().matching("com.dais39.sample.app.(*)..").should().beFreeOfCycles()

    rules.check(classes)
}

en conclusion

Qu'est-ce que ArchUnit cette fois? Que pouvez-vous faire avec ArchUnit? Je l'ai présenté. Je recommande personnellement vivement cette bibliothèque pour sa capacité à tester l'architecture et la prise en charge de Kotlin, j'espère donc que plus de gens seront intéressés par cet article.

Matériel de référence

Recommended Posts

Première unité d'arche
Premier poste
Premier poste
Premier poste
Premier java.util.logging