Library / SDK
TNG/ArchUnitNET avatar
TNG/ArchUnitNET

ArchUnitNET for C# architecture testing

A C# architecture test library to specify and assert architecture rules in C# for automated testing.

1,390 stars84 forksC#Apache-2.0

At a glance

What is it?
ArchUnitNET turns architectural dependencies and coding constraints into C# tests. It fits .NET teams that want automated checks for layer boundaries and forbidden calls, with test-framework packages for xUnit, NUnit, MSTest, and TUnit.
Who is it for?
ArchUnitNET is appropriate for .NET teams with architectural boundaries that can be expressed as deterministic dependency, type, interface, or method-call rules. It is a poor fit for projects that need architecture decisions inferred from source text alone, dynamic behavior tests, or rules the loaded assemblies cannot represent.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

ArchUnitNET targets enforceable .NET boundaries

ArchUnitNET addresses a gap that ordinary unit tests leave open: a class may pass its own functional tests while still pointing in the wrong architectural direction. The library lets a .NET team express constraints such as example types residing in a particular layer, interfaces occupying a designated namespace, or one layer not depending on another. The intended users are C# developers who want those constraints checked automatically with their test suite rather than relying only on review conventions.

The supported objects extend beyond a simple class count. The README describes dependencies among classes, members, interfaces, and other imported C# structures. Rules can select types by assembly or namespace, select classes by implemented interfaces, reject dependencies, inspect method members, and check naming conditions. That makes the library useful for boundary tests that are too broad for a local code-style check and too structural for a functional test.

ArchUnitNET does not decide what an architecture should be. Developers still need to define the relevant assemblies, namespaces, types, interfaces, methods, and allowed relationships. Its contribution is to turn those definitions into executable assertions and surface violations through the chosen test framework. This distinction matters during adoption: teams with explicit architectural boundaries have usable inputs, while teams whose architecture exists mainly as undocumented knowledge may need to formalize it before they can write useful rules.

Assembly import drives ArchUnitNET rule checks

ArchUnitNET analyzes C# bytecode and imports the analyzed classes into its own C# code structure. Its loader builds an `Architecture` from explicitly loaded assemblies. The README example loads both an allowed assembly and a forbidden assembly, then builds that shared object once in a static field. Each architecture rule receives the resulting `Architecture` through `Check(Architecture)`.

The data flow starts with compiled .NET assemblies, not with a textual scan of source files. `ArchLoader.LoadAssemblies(...)` receives `System.Reflection.Assembly` instances, and `Build()` produces the queryable architecture model. Fluent selectors such as `Types()`, `Classes()`, and `Interfaces()` create providers. Conditions such as `ResideInAssembly`, `ResideInNamespace`, `ImplementInterface`, `AreAssignableTo`, and `HaveNameContaining` refine those providers. Assertions then apply expectations including `Be`, `NotDependOnAny`, and `NotCallAny`.

A rule can stand alone or be joined through `And(...)`. The library also supports attaching a reason with `Because(...)`, which the README says is displayed with a failure and the relevant violating types. Because the rules operate on an imported model, assembly identity and compiled metadata are central to correctness. A rule can only evaluate what the loader successfully imports, and the documentation's reference to known Debug artifact edge cases indicates that build configuration is not an incidental detail.

NuGet setup and a first xUnit dependency rule

The README gives installation through the PowerShell `Install-Package` command. Teams using xUnit, NUnit, or MSTestV2 should install the corresponding ArchUnitNET extension rather than selecting only the core package. The following commands are copied from the README and represent the available integration packages:

powershell
PS> Install-Package TngTech.ArchUnitNET
PS> Install-Package TngTech.ArchUnitNET.xUnit
PS> Install-Package TngTech.ArchUnitNET.xUnitV3
PS> Install-Package TngTech.ArchUnitNET.NUnit
PS> Install-Package TngTech.ArchUnitNET.MSTestV2

Choose the extension that matches the unit-test framework already used by the project. The first real use is to load the assemblies under examination, define the prohibited dependency, and check it through a test. This abbreviated form uses the documented API, while the assembly and namespace names must be replaced with real values from the consuming project:

csharp
private static readonly Architecture Architecture = new ArchLoader().LoadAssemblies(
    System.Reflection.Assembly.Load("ExampleClassAssemblyName"),
    System.Reflection.Assembly.Load("ForbiddenClassAssemblyName")
).Build();

private readonly IObjectProvider<IType> ExampleLayer =
    Types().That().ResideInAssembly("ExampleAssembly").As("Example Layer");

private readonly IObjectProvider<IType> ForbiddenLayer =
    Types().That().ResideInNamespace("ForbiddenNamespace").As("Forbidden Layer");

[Fact]
public void ExampleLayerShouldNotAccessForbiddenLayer()
{
    Types().That().Are(ExampleLayer).Should()
        .NotDependOnAny(ForbiddenLayer).Because("it's forbidden")
        .Check(Architecture);
}

The expected successful result is a passing test when no selected type in `ExampleLayer` depends on a selected type in `ForbiddenLayer`. If it fails, the rule's reason and violating types should appear in the test output according to the README.

Debug binaries constrain ArchUnitNET deployment

The README recommends running ArchUnitNET-based tests with `dotnet test -c Debug`. This is not simply a preference for a convenient build profile. ArchUnitNET reads the architecture from analyzed binaries, so the contents and shape of those binaries affect what it can inspect. The documentation links to a page about known edge cases under Debug artifacts.

That recommendation creates a direct operational constraint. Teams should not silently switch architecture tests to Release without checking the documented edge cases and their own build output. Conditional compilation, optimizer behavior, generated assemblies, or absent debug artifacts could affect an analysis whose inputs are compiled assemblies. The README does not enumerate every affected case, so it would be unsupported to claim that Release always fails or that Debug completely eliminates all analysis differences.

The command to execute the tests is documented directly:

bash
dotnet test -c Debug

The reader should see the normal test result from the selected test framework. A passing result confirms that the loaded model satisfies the stated rule, not that the complete architecture is correct. Coverage depends on selecting the right assemblies and encoding every boundary the team intends to enforce. ArchUnitNET can reject a modeled dependency, but it cannot report an omitted assembly or a layer rule that the test suite never defines.

ArchUnitNET differs from source-level conventions and Java ArchUnit

A source formatter or analyzer is a different kind of tool. ArchUnitNET reads compiled C# bytecode, imports types into an architecture model, and evaluates relationships such as dependencies and method calls. A formatter or conventional linter generally works with source syntax and reports style or local code violations. The README does not compare ArchUnitNET with those tools, so no claim about a specific formatter's capabilities should be inferred. The meaningful distinction is the problem being tested: ArchUnitNET is aimed at architecture and coding rules involving relationships among code elements.

ArchUnitNET also identifies itself as the C# fork of ArchUnit for Java. Java ArchUnit is therefore the closest stated alternative. The practical approach differs at the runtime boundary: Java ArchUnit targets Java projects, while ArchUnitNET loads C# assemblies and offers the documented NuGet packages for .NET test ecosystems. Teams already standardized on the Java approach for a Java codebase should retain that tool there. A mixed estate can use the architecture-testing model in both languages, but the README does not claim identical rule catalogs or one-way rule compatibility, so cross-language rule parity should not be assumed.

This makes ArchUnitNET a closer fit than a text convention checker when the condition depends on compiled relationships. It is not a replacement for runtime integration tests. It also does not document configuration-file rules, source comments, or architectural governance, which are separate concerns outside the shown API.

Release cadence and Apache-2.0 adoption costs

The repository is not archived, and the last push was on 2026-10-15. Recent listed releases are `0.13.4` on 2026-08-19, `0.13.3` on 2026-03-05, and `0.13.2` on 2026-01-23. These dates indicate recent repository activity, but they do not by themselves describe the size of each release, migration work, or compatibility impact.

The upgrade cost that can be stated from the README is therefore limited. Developers must choose the core package or the extension corresponding to their test framework, keep the architecture loading aligned with test-project assembly references, and account for the documented Debug artifact constraints. Before changing versions, the consuming team would need release-specific migration information, which the README does not provide. It would also need to verify analyzer coverage against its own dependencies, especially if a previously implicit boundary is not represented in the loaded assembly set.

ArchUnitNET is published under Apache License 2.0, with the repository also containing `LICENSE`, `Licenses/`, and `NOTICE` entries. The project identifies `TngTech.ArchUnitNET` as the core NuGet package and separate extension packages for test frameworks. That packaging creates a dependency choice rather than a single universal package. Licence obligations and notices should be reviewed under Apache-2.0 for the consuming distribution, but this article does not provide legal interpretation.

Editorial conclusion

ArchUnitNET is appropriate for .NET teams with architectural boundaries that can be expressed as deterministic dependency, type, interface, or method-call rules. It is a poor fit for projects that need architecture decisions inferred from source text alone, dynamic behavior tests, or rules the loaded assemblies cannot represent. Verify the Debug configuration, target assembly loading, supported test framework package, expected rule semantics, and any project-specific limitations before adding the rules to a blocking build.

Frequently asked questions

Which NuGet package should I install for ArchUnitNET with xUnit?

The README lists `TngTech.ArchUnitNET.xUnit` for xUnit and `TngTech.ArchUnitNET.xUnitV3` for xUnitV3. It also lists separate packages for NUnit and MSTestV2.

How should I run ArchUnitNET tests?

The README recommends `dotnet test -c Debug` because ArchUnitNET reads architecture information from analyzed binaries. It links to documentation about known Debug artifact edge cases.

Can ArchUnitNET check whether one layer depends on another?

Yes. The documented fluent API combines a selected source type provider with `NotDependOnAny(...)`, allowing a rule to reject dependencies between defined layers.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. TNG/ArchUnitNET on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tng-archunitnet.svg)](https://hysenlabs.com/projects/tng-archunitnet)

Was this useful?