Architekturregeln mit Java und ArchUnit sicherstellen

Über ArchUnit

Eine Herausforderung in Software-Projekten besteht oft darin, spezifische Architekturregeln sicherzustellen. Textuelle Formen der Dokumentation und Diagramme sind im Arbeitsalltag wenig präsent, und so schleichen sich im Laufe des Projektlebenszyklus häufig Verletzungen dieser festgelegten Regeln ein.

ArchUnit ist eine relativ junge Bibliothek, die es Entwicklern erlaubt, Architekturregeln in Form ausführbarer Tests zu definieren und mit gängigen Werkzeugen und Test-Frameworks wie Gradle, Maven, JUnit, Surefire & Co. auszuführen. Diese Form der lebendigen Dokumentation erleichtert es, die Konsistenz der Regeln zu erhalten und Abweichungen davon zumindest bewusst in Kauf zu nehmen, statt später zufällig darauf zu stoßen.

Im folgenden Kurzbeispiel wollen wir einen Architekturtest mit ArchUnit erstellen; als Build-Werkzeug kommt Gradle zur Anwendung.

Integration

Um ArchUnit nutzen zu können, müssen wir unserer Konfigurationsdatei build.gradle lediglich eine Abhängigkeit hinzufügen:

testCompile 'com.tngtech.archunit:archunit-junit:0.4.0'

Architekturtests schreiben

Wir nehmen eine Architektur an, in der wir zwei Packages names net.seibertmedia.a und net.seibertmedia.b haben. Nun wollen wir verhindern, dass Klassen aus dem Sub-Package a auf das Sub-Package b zugreifen.

Dank der Fluent-API ist der Einstieg schnell bewerkstelligt und eine Lösung sieht wie folgt aus:

package architecture;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.lang.ArchRule;
import org.junit.Before;
import org.junit.Test;

public class ArchitecturalConstraintsTest {

  private final ClassFileImporter importer = new ClassFileImporter();
  private JavaClasses classesAnalyzed;

  @Before
  public void importClasses() {
    classesAnalyzed = importer.importPackages("net.seibertmedia");
  }

  @Test
  public void classesFromPackageAMustNotAccessClassesInPackageB() throws Exception {
    ArchRule rule = noClasses().that().resideInAPackage("net.seibertmedia.a..")
        .should().accessClassesThat().resideInAPackage("net.seibertmedia.b..");
    rule.check(classesAnalyzed);
  }

}

Wenn wir mittels gradle test nun die Tests ausführen, bekommen wir – sofern beispielsweise ein solcher Zugriff erfolgt ist – eine entsprechende Fehlermeldung:

% gradle test --info 1
[..]
Gradle Test Executor 1 started executing tests.

architecture.ArchitecturalConstraintsTest > classesFromPackageAMustNotAccessClassesInPackageB STANDARD_ERROR
Gradle Test Executor 1 finished executing tests.

architecture.ArchitecturalConstraintsTest > classesFromPackageAMustNotAccessClassesInPackageB FAILED
 java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a package 'net.seibertmedia.a..' should access classes that reside in a package 'net.seibertmedia.b..'' was violated:
 Method <net.seibertmedia.a.ClassInPackageA.<init>()> calls constructor <net.seibertmedia.b.ClassInPackageB.<init>()> in (ClassInPackageA.java:7)
 at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:81)
 at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:153)
 at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:75)
 at com.tngtech.archunit.lang.syntax.ClassesShouldThatInternal.check(ClassesShouldThatInternal.java:294)
 at architecture.ArchitecturalConstraintsTest.classesFromPackageAMustNotAccessClassesInPackageB(ArchitecturalConstraintsTest.java:27)

1 test completed, 1 failed
Finished generating test XML results (0.011 secs) into: /data/project/smedia-archunit-beispiele/build/test-results
Generating HTML test report...
Finished generating test html results (0.017 secs) into: /data/project/smedia-archunit-beispiele/build/reports/tests

Ausführlichere Informationen zur ArchUnit-Syntax, zur Konfiguration und zu weiteren konkreten Beispielen finden sich in der Projekt-Dokumentation auf GitHub und diesem weiterführenden Blog-Artikel.

Ihr Partner für individuelle Software-Projekte

Planen Sie bereits ein konkretes Software-Projekt? Oder gibt es bestimmte Prozesse in Ihrem Unternehmen, die Ihnen schon lange Kopfzerbrechen bereiten? Bremst ein System oder eine Schnittstelle Ihre Mitarbeiter auf der einen oder Ihre Kunden auf der anderen Seite aus? Dann sprechen Sie mit uns darüber! Wir freuen uns darauf, gemeinsam eine individuelle Lösung zu entwickeln – bei höchster Qualität und voller Kostenkontrolle.

Weiterführende Infos

Was agile Software-Projekte dem Kunden bringen
Vorgehen nach Scrum: Die beste Wahl für den Kunden, die Agentur – und das Produkt
Echte Integration: Das Zusammenspiel von JIRA, Stash und Bamboo im Entwicklungsprozess
Der Beginn eines Happy Ends: Initialer Anforderungs-Workshop für erfolgreiche Projekte
Darum ist eine regelmäßige Kundenpräsenz beim Entwicklungsteam so sinnvoll

Artikel teilen:Share on FacebookTweet about this on TwitterShare on Google+Share on LinkedInEmail this to someonePrint this page