SECRET OF CSS

ArchUnit: Test Software Architecture Easily


How to validate architectural constraints in Java projects using ArchUnit

A computer with a notebook, pen, and a cup of coffee on the table.
Photo by Andrew Neel on Unsplash

As projects grow, the architecture becomes more complex. Each project has standard rules that developers need to follow. If new colleagues join the project, they might violate the architectural constraints without even knowing about it. If everyone adds new code wherever they think it fits, the code base will become messy.

It leads to laborious code maintenance and opens doors to bugs. Of course, there might be a senior developer who had been working on the project for years. They review the new code periodically and let the others know if they should fix anything.

The problem is that this requires manual intervention, and sometimes humans don’t spot all the issues. The best way of protecting the software architecture from violations is to have an automated process.

In this article, I’ll showcase the ArchUnit framework that tackles such issues. You’ll see typical practical examples to understand how to integrate this tool into your projects.

Let’s dive into it!

ArchUnit serves for unit testing Java projects architecture. It validates the architectural constraints. Usually, an architect or a lead developer establishes the general patterns. They write the rules in human-readable unit tests that can be evaluated with any unit testing tool like JUnit.

When someone breaks the rules, the tests will fail. Developers will see information about the issue. It ensures that the codebase stays intact and everyone follows the guidelines.

This art of testing is especially beneficial for new joiners to understand the project structure.

I assume you already have a Java project for testing, for example, a SpringBoot project.

First, add the ArchUnit maven dependency to your pom.xml:

<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>0.23.1</version>
<scope>test</scope>
</dependency>

Now let’s take a look into some common use cases.

Create a new Java class, for example, ArchUnitTest.java. We’ll place all the tests there for the sake of simplicity.

Naming conventions tests

If you have multiple modules, you can point which package to scan using the annotation AnalyzeClasses.

For example, you might want to be consistent in all SpringBoot projects that the Application class name should be “MainApp”:

As you can see, the code is self-explanatory. You only have to provide the annotation ArchUnit needs to validate against, and the verification works out-of-the-box.

You can check if all Controller classes have the suffix “Controller”:

Controllers should be suffixed test case

Note that the allowEmptyShould(true) option allows tests to fail if the given project contains no controllers.

Package location tests

You might want to check if the entities reside in the “model” package:

Note that you could have the following structure:

1*OrzeB7V7jfSd5vg9VS1KOw
Model and subpackages

The test will be valid because the entities reside in the “entity” which is under the “model” package.

Similarly, you can check if the configuration classes reside in the “config” package:

Configuration files should reside in the config package test case

Note that you can easily chain multiple conditions if you need to make the rule more sophisticated.

If you want to validate all classes that extend a parent class, you can achieve it this way:

The xml requests should reside in the request package test case

In this example, all classes that extend the SoapRequest.class will be validated.

Annotation tests

The following example illustrates the check for a valid annotation:

Configurations should be annotated test case

All configuration classes should have either of the desired annotations.

Software design pattern tests

Let’s say that your project should follow the Onion architecture style:

Onion architectural pattern test case

Test exclusions

Sometimes you don’t want to execute the rules for certain classes. Typically, for JUnit tests.

This can be easily achieved by the following:

Test exclusions option

Or maybe you have a class you want to ignore because the rules don’t apply to it. Create a custom rule and import it, as shown in the above example.

Specific test exclusions option

Congratulations! You now know why testing the architecture is essential.

You learned how to integrate the ArchUnit testing framework into your Java projects. You also got familiar with typically used rules that you can apply in your architectural guidelines.

Check out the References section below for more rule examples.

I hope that this tutorial has been helpful. If you want to read more about software architecture best practices, you might like my other article:

Thanks for reading, and happy coding!



News Credit

%d bloggers like this: