Test your architecture with NetArchTest
Do you have an architecture type defined on the project? Onion / Clean / Vertical Slice / … architecture? Or do you have your own architecture rules? How do you verify that they are followed?
We have .editorconfig
, Roslynator, SonarCloud, … But this is not enough for architecture.
A solution may be to use the NetArchTest library. This library allows you to define different rules for the architecture and then continuously validate them using unit tests.
What are the rules? Preferably with examples:
- the domain layer must not contain dependencies on other layers
- access to the database can only be in the infrastructure layer
- value objects must be immutable
- repositories must have the
Repository
suffix - DTO classes must not be used in the domain and infrastructure layers
async
methods must not have a return type ofvoid
and suffixAsync
- and many other rules that may be important on your project
How to do it?
- Add a reference to the
NetArchTest
library to the test project.
dotnet add package NetArchTest.Rules
- We will create unit tests to verify our rules.
[Fact]
public void RepositoriesShouldBeLocatedInInfrastructureNamespace()
{
var result = Types.InAssembly(typeof(ProductRepository).Assembly)
.That()
.ImplementInterface(typeof(IRepository)) // 👈 rule for repositories
.Should()
.ResideInNamespaceEndingWith("Infrastructure") // 👈 use rule
.GetResult();
result.IsSuccessful.Should().BeTrue();
}
[Fact]
public void DomainShouldNotReferenceInfrastructure()
{
var result = Types.InAssembly(typeof(ProductDto).Assembly)
.That()
.ResideInNamespace("EShop.Domains")
.ShouldNot()
.HaveDependencyOn("EShop.Infrastructure")
.GetResult();
result.IsSuccessful.Should().BeTrue();
}
- We can also create our own rules.
public class IsRecordRule : ICustomRule
{
// 👇 use custom rule for checking if type is Record
public bool MeetsRule(TypeDefinition type)
=> type.GetMethods().Any(m => m.Name == "<Clone>$");
}
public static class CustomRules
{
// 👇 extension method to simplify the use of a custom rule
public static ConditionList BeRecord(this Conditions conditions)
=> conditions.MeetCustomRule(new IsRecordRule());
}
[Fact]
public void DtoShouldBeRecordType()
{
var result = Types.InAssembly(typeof(ProductDto).Assembly)
.That()
.HaveNameEndingWith("Dto")
.Should()
.BeRecord()
.GetResult();
result.IsSuccessful.Should().BeTrue();
}