I wrote before about (anti)patterns in package dependencies. And of course the regular reader of my blog knows about Degraph, my private project to provide a visualization for package dependencies which can help a lot when you try to identify and fix such antipatterns.

But instead of fixing a problem we all probably prefer preventing the problem in the first place. Therefore in the latest version Degraph got a new feature: A DSL for testing Dependencies.

You can write tests either in Scala or in Java, whatever fits better into your project.

A typical test written with ScalaTest looks like this:

classpath // analyze everything found in the current classpath
.including("de.schauderhaft.**") // only the classes that start with "de.schauderhaft."
.withSlicing("module", "de.schauderhaft.(*).**") // use the third part of the package name as the module name, and make sure the modules don't have cycles
.withSlicing("layer",
("persistence","de.schauderhaft.legacy.db.**"), // consider everything in the package de.schauderhaft.legacy.db and subpackages as part of the layer "persistence"
"de.schauderhaft.*.(*).**") // for everything else use the fourth part of the package name as the name of the layer
) should be(violationFree) // check for violations (i.e. dependency circles)

The equivalent test code in Java and JUnit looks like this:

assertThat(
classpath() // analyze everything found in the current classpath
.including("de.schauderhaft.**") // only the classes that start with "de.schauderhaft."
.withSlicing("module", "de.schauderhaft.(*).**") // use the third part of the package name as the module name, and make sure the modules don't have cycles
.withSlicing("layer",
new NamedPattern("persistence","de.schauderhaft.legacy.db.**"), // consider everything in the package de.schauderhaft.legacy.db and subpackages as part of the layer "persistence"
"de.schauderhaft.*.(*).**") // for everything else use the fourth part of the package name as the name of the layer
),
is(violationFree())
);

You can also constrain the ways different slices depend on each other. For example

...
.withSlicing("module", "de.schauderhaft.(*).**").allow(oneOf("order", "reporting"), "customer", "core")
...

Means:

  • stuff in de.schauderhaft.order may depend on de.schauderhaft.customer and de.schauderhaft.core
  • the same is true for de.schauderhaft.reporting
  • de.schauderhaft.customer may depend on de.schauderhaft.core
  • all other dependencies between those packages are disallowed
  • packages from and to other packages are allowed


If you also want to allow dependencies between the order slice and the reporting slice replace oneOf with anyOf.

If you want to disallow dependencies from reporting or order to core you can replace allow with allowDirect.

See the official documentation for more details, especially all the options the DSL offers, the imports needed and how to set up Degraph for testing.

I'm trying to get Degraph into maven central to make usage inside projects easier.
I also have some changes to the testing DSL on my to-do list. And finally I'm working on a HTML5 based front end. So stay tuned.

And as always: Feedback including feature requests and pull requests is welcome.

Talks

Wan't to meet me in person to tell me how stupid I am? You can find me at the following events: