In this tutorial, I will show you how to unit test architecture of your Java application. This testing can be helpful when implementing many ideas from various designs(implementing hexagonal architecture, Domain Driven Design, CQRS, etc.)

What is the problem that we want to solve?

Let’s say that we are following Domain Driven Design(DDD) and we want to make sure that our domain is isolated. We want to make sure that all classes that are inside the domain package are not dependent on classes that are inside the infrastructure package.

Another example would be that we are following three tier architecture with three layers: web, application, and database. We want to make sure that web layer depends onthe application layer, and the application depends on the database layer. Let’s write tests for those two examples!

Adding archunit dependencies

Let’s follow the officiel guide:

dependencies {
    testImplementation 'com.tngtech.archunit:archunit:1.3.0'
    testImplementation("com.tngtech.archunit:archunit-junit5:1.3.0")
}

Important: You need only one of those dependencies. If you use JUnit 5 then you need archunit-junit dependency, otherwise you need archunit dependency. In my example, I will use JUnit 5.

Writing archunit test

We can find plenty of great examples here on how to write archunit tests. We can use those examples for our use case:

@AnalyzeClasses(packages = "com.javamentor")
public class ArchitectureTest {

    @ArchTest
    private final ArchRule isolatedDomainTest =
            noClasses().that().resideInAPackage("..domain..")
                    .should().dependOnClassesThat().resideInAPackage("..infrastructure..");

    @ArchTest
    private final ArchRule webCannotDependOnDatabaseLayerTest =
            noClasses().that().resideInAPackage("..web..")
                    .should().dependOnClassesThat().resideInAPackage("..database..");

    @ArchTest
    private final ArchRule databaseCannotDependOnApplicationLayerTest =
            noClasses().that().resideInAPackage("..database..")
                    .should().dependOnClassesThat().resideInAPackage("..application..");
    @ArchTest
    private final ArchRule loggers_should_be_private_static_final =
            fields().that().haveRawType(Logger.class)
                    .should().bePrivate()
                    .andShould().beStatic()
                    .andShould().beFinal()
                    .because("we agreed on this convention");
}

Besides those “classes from this package should not depend on classes from the other package” I added one more rule that say that Logger has to be private and static. There are plenty of other great examples on how to write good tests such as: “I want to prohibit use of java.util.Date class(because this class is deprecated)” or “classes with this suffix has to be protected and final“, etc. Let’s try to violate this rule in our code and see if test will fail.

package com.javamentor.web;

import com.javamentor.database.PersonDatabase;

public class PersonController {
    private PersonDatabase personDatabase;
}


package com.javamentor.database;

public class PersonDatabase {
}

We create two classes and obviously break the rule. If we start our test the result will be:

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a package '..web..' should depend on classes that reside in a package '..database..'' was violated (1 times):
Field <com.javamentor.web.PersonController.personDatabase> has type <com.javamentor.database.PersonDatabase> in (PersonController.java:0)

Conclusion

We learned how to unit test architecture of our application. As I said in the beggining, this can be helpful when implementing many ideas from various designs(implementing hexagonal architecture, Domain Driven Design, CQRS, etc.)

Further reading

If you want to learn more about testing, those resources are the best:

  • Detailed theoretical explanation and practical implementation of the Test Pyramid is on the Martin Fowler blog here
  • Great presentation regarding testing in microservices
  • Chapter “Testing patterns” in the Microservices Patterns book by Chris Richardson
  • Testing category on my blog