Architecture Unit Tests
Architecture Unit Tests are tests for checking the architecture you are using is respected at any time. Such tool can check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more.
Why?
Those kind of tests can act as a safety net that helps teams (and newjoiners) to :
- Maintain clear architecture boundaries
- Your team rules can easily be broken by inadvertence
- Document your team architecture decisions (at least structural ones)
- With a clear DSL
- Automate architecture checks and ensure to respect them at any point in time
- Out-of the box integrated in your test suite and so your CI 😉
Problems
How could we ensure we respect our architecture decisions?
How to
You can really make your architecture and its related rules explicit through your test suite by describing architecture rules.
You can use ArchUnit for that, it is pretty well integrated with JUnit in java and xUnit in .NET.
You can automate checks like :
-
Inward dependency rule
Use Cases--->Domainbut the opposite is forbidden
-
Cycle dependencies
- class
Adepends onBthat depends onCthat depends onA - This kind of dependencies are pretty hard to identify
- class
-
Namespace containment rules :
- every
Controllerclasses should be contained a given namespace
- every
-
Keep your Domain "pure"
- No technical related stuff -> attribute / annotations
You can also check other rules in your code :
- Naming conventions
- Coding guidelines :all the interface should start with a big I in C# for example
- Return types for public methods on given classes
- Check that your Controller public methods all return your own API Envelop for example
- Detect some linguistic anti-patterns
is/hasfunctions should return abooleangetfunctions should not returnvoid- Other anti-patterns can be checked
ArchUnit in C#
Here are the steps to follow to implement some rules in C# :
- Add project dependencies
<PackageReference Include="TngTech.ArchUnitNET.xUnit" Version="0.10.1" />
<PackageReference Include="TngTech.ArchUnitNET" Version="0.10.1" />
- Create an
ArchUnitExtensionsclass to make your life easier :
public static class ArchUnitExtensions
{
private static readonly Architecture Architecture =
new ArchLoader()
// You can load as many as assemblies as you want
.LoadAssemblies(typeof(<ATypeFromAFirstProject>).Assembly)
.Build();
public static void Check(this IArchRule rule) => rule.Check(Architecture);
}
- Describe your architecture rules using the DSL :
- Do not forget to call the
Checkmethod to run the rule / hypothesis against your currentArchitecture
- Do not forget to call the
private static GivenMethodMembersThat Methods()
=> MethodMembers()
.That()
.AreNoConstructors()
.And();
[Fact]
public void NoGetMethodShouldReturnVoid() =>
Methods()
.HaveNameMatching("Get[A-Z].*", useRegularExpressions: true).Should()
.NotHaveReturnType(typeof(void))
.Check();
private static GivenTypesConjunction DomainTypes() =>
Types()
.That()
.ResideInNamespace(Namespaces.Domain, true);
[Fact]
public void DomainCanOnlyAccessDomainItselfAndExceptions() =>
DomainTypes()
.Should()
.OnlyDependOnTypesThat()
.ResideInNamespace(Namespaces.Domain, true)
.Check();
Constraint
Identify an architecture rule your want to ensure in your code :
- Create your first Architecture rule
- Automate its check through your favorite test runner