It is highly desirable to have acyclic dependencies between the packages of a project. This often leads to describing the packages structure as “layered”: each package on a layer can only depend on a package on a lower layer.
First you need to define the packages and their expected dependencies. Mix-in the org.specs2.specification.Analysis trait and define layers (taking
layers (
"runner",
"reporter",
"specification mutable",
"mock form",
"matcher",
"execute",
"reflect xml time html",
"collection control io text main data").withPrefix("org.specs2")
The above expression defines layers as an ordered list of Strings containing space-separated package names. It is supplemented by a withPrefix declaration to factor out the common package prefix between all these packages.
By default, the packages are supposed to correspond to directories in the src/target/scala-<version>/classes directory. If your project has a different layout you can declare another target directory:
layers("...").inTargetDir("out" / "classes")
Every rule has exceptions :-). In some rare cases, it might be desirable to exclude a class from being checked on a given layer. To do this, you can use the include/exclude methods on the Layer class:
layers (
"runner",
"reporter",
"specification mutable".exclude("mutable.SpecificationWithJUnit"),
"mock form",
"matcher",
"execute",
"reflect xml time html",
"collection control io text main data").withPrefix("org.specs2")
The include/exclude methods accept a list of regular expressions to:
exclude will be necessary)Now you’ve defined layers, you can use the beRespected matcher to check if all the dependencies are verified:
val design = layers("...")
design must beRespected
If some dependencies are not respected:
[error] those dependencies are not satisfied:
[error] org.specs2.main x-> org.specs2.io because org.specs2.io.FileSystem -> org.specs2.main.Arguments
[error] org.specs2.main x-> org.specs2.io because org.specs2.io.FileSystem -> org.specs2.main.ArgumentsArgs
The org.specs2.specification.Analysis trait allows to directly embed the layers definition in a Specification and turn it into an Example:
class DependenciesSpec extends Specification with specification.Analysis { def is =
"this is the application design" ^
layers(
"gui commandline",
"controller",
"backend"
)
}