Testujte vašu architektúru pomocou NetArchTest

Máte definovaný na projekte typ architektúry? Onion / Clean / Vertical Slice / ... architektúru?
Prípadne máte vlastné architektonické pravidlá?
Ako si overujete že sú dodržiavané?

Máme .editorconfig, Roslynator, SonarCloud, ... Ale na architektúru toto nestačí.

Riešením môže byť použitie knižnice NetArchTest.
Táto knižnica umožňuje definovať rôzne pravidlá pre architektúru a následne ich priebežne overovať pomocou unit testov.

O aké pravidlá sa jedná? Najlepšie na príkladoch:

  • doménova vrstva nesmie obsahovať závislosti na iných vrstvách
  • prístup k databáze môže byť iba v infraštruktúrnej vrstve
  • value objekty musia byť immutable
  • repositáre musia mať sufix Repository
  • DTO triedy nesmú byť použité v doménovej a infraštruktúrnej vrstve
  • async metódy nesmú mať návratový typ void a suffix Async
  • a mnoho ďalších pravidiel ktoré na vašom projekte môžu byť dôležité

Ako na to?

  • Pridáme referenciu na knižnicu NetArchTest do testovacieho projektu.
dotnet add package NetArchTest.Rules
  • Vytvoríme unit testy, ktoré budú overovať naše pravidlá.
[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();
}
  • Môžeme si vytvoriť aj vlastné pravidlá.
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();
}

Celé demo
Dokumentácia