NetArchTest Documentation

repository·master·Indexed 23 days ago

https://github.com/benmorris/netarchtest

A fluent API for .NET Standard that enables developers to enforce architectural rules, such as naming conventions, class design, and dependency constraints, within unit tests. It provides tools for selecting types, applying predicates, asserting conditions, and grouping rules into policies via the NetArchTest.Rules NuGet package.

Tokens
1.2K
Snippets
2
Records
5
Agent score
33%

What's inside NetArchTest

  1. How to write architectural rules using the fluent API

    master

    NetArchTest uses a fluent API to define rules for class design, naming, and dependencies. The workflow follows these steps:

    1. Select Types: Start with the static Types class to load types from an assembly, namespace, or the current domain.
    2. Filter (Predicates): Use .That() followed by predicates like .ResideInNamespace(), .ImplementInterface(), or .AreClasses() to select a subset of types. Chain multiple predicates using .And() or .Or().
    3. Assert (Conditions): Use .Should() or .ShouldNot() to define the architectural requirement (e.g., .BeSealed(), .HaveDependencyOn()).
    4. Execute: Use .GetResult() to check if the rule was met, or .GetTypes() to retrieve the specific types that matched the selection.
  2. Common rule examples

    master

    Here are common patterns for enforcing architectural conventions:

    Preventing layer violations (Dependency checking):

    var result = Types.InCurrentDomain()
        .That()
        .ResideInNamespace("NetArchTest.SampleLibrary.Presentation")
        .ShouldNot()
        .HaveDependencyOn("NetArchTest.SampleLibrary.Data")
        .GetResult()
        .IsSuccessful;

    Enforcing interface implementation:

    var result = Types.InCurrentDomain()
        .That().HaveDependencyOn("System.Data")
        .And().ResideInNamespace(("ArchTest"))
        .Should().ResideInNamespace(("NetArchTest.SampleLibrary.Data"))
        .GetResult()
        .IsSuccessful;

    Enforcing class modifiers (e.g., Sealed):

    var result = Types.InCurrentDomain()
        .That().ImplementInterface(typeof(IWidgetService))
        .Should().BeSealed()
        .GetResult()
        .IsSuccessful;
  3. Grouping rules into Policies

    master

    Use the Policy class to group multiple architectural rules into a single named policy. This is useful for managing complex architectures and generating reports.

    • Define: Use Policy.Define(name, description) to start a policy.
    • Scope: Use .For(TypeSelector) to define the scope of types the policy applies to.
    • Add Rules: Use .Add() to attach rules. Each .Add() call takes a lambda returning a rule, a rule name, and a rule description.
    • Evaluate: Rules are executed lazily when .Evaluate() is called, returning a PolicyResults object.
    var architecturePolicy = Policy.Define("Example Policy", "This is an example policy")
                    .For(Types.InCurrentDomain)
                    .Add(t =>
                       t.That()
                       .ResideInNamespace("NetArchTest.SampleLibrary.Presentation")
                       .ShouldNot()
                       .HaveDependencyOn("NetArchTest.SampleLibrary.Data"),
                       "Enforcing layered architecture", "Controllers should not directly reference repositories"
                    )
                    .Add(t =>
                        t.That()
                        .AreInterfaces()
                        .Should()
                        .HaveNameStartingWith("I"),
                        "Generic implementation rules", "Interface names should start with an 'I'"
                    );
    
    // Execute the policy
    var results = architecturePolicy.Evaluate();
  4. Writing custom rules with ICustomRule

    master

    You can extend NetArchTest by implementing the ICustomRule interface. Custom rules can be used as both predicates (to select types) and conditions (to assert properties) using the .MeetCustomRule() method.

    var myRule = new CustomRule(); // Implements ICustomRule
    
    var result = Types.InCurrentDomain()
        .That().AreClasses()
        .Should()
        .MeetCustomRule(myRule)
        .GetResult().IsSuccessful;