This solution is a small sample that demonstrates snapshot-based architecture testing for a .NET 9 codebase.
- The primary purpose of the repository is the
Tests.Architectureproject (Tests.Architecture.csproj), which contains the architecture snapshot tests that validate the intended structure and dependencies of the codebase. - Two supporting test projects,
Tests.ArchUnit.BaseandTests.Verify.Base, show the basic steps and utilities used by the architecture tests (for example, composing ArchUnitNET rules and creating Verify snapshots).
For more details on working with these Tests, reference the Working With Tests section
- The non-test projects are organized to represent a typical layered architecture:
Domaincontains core business logic and domain entities.Applicationcoordinates application workflows and uses domain logic.Infrastructureprovides implementations for data access and external integrations.Application.ContractsandInfrastructure.Contractsdefine interfaces and contracts for their respective layers, supporting loose coupling and testability.Hosthouses WebAPI endpoints and acts as a composition root, wiring up dependencies and hosting the application for testing purposes.
The architecture in this solution is currently set up in the following way:

The red arrows show unintended or undesirable references in the current architecture.
The DTO arrow means we want Application to convert Domain Entities into external DTOs. This does not yet happen. We return Domain objects, so we have an undesirable reference to Domain.
The CityService arrow means we are mistakenly using CityService in Application instead of the ICityService Interface. Using the interface from contracts would allow us to remove the direct Infrastructure reference.
The black arrows show intended references as to our target architecture.
Use the test projects as the starting point to learn how the snapshot-based architecture testing is arranged and how rules and snapshots are composed and verified.
All except one test in VerifyTests.cs are currently working. To run them successfully, you need to run them once. This creates empty verified files and received files with contents. Copy the content of the received file to the verified file, save, and rerun the tests. Once verified files are created, you will create a successful test run. If a new change is introduced, you will receive a diff between these two files again, and should move over the new contents if the changes are correct.
Example:
- Run Tests
- VerifyTests.BasicVerifyTest_WithVerifySettings.received.json (new content)
- VerifyTests.BasicVerifyTest_WithVerifySettings.verified.json (empty)
- Copy content from received to verified
- Rerun tests
You can use the tool DiffEngineTray to more easily work with verified and received files:
dotnet tool install -g DiffEngineTray
The Test "BasicVerifyTest_WithVerifySettingsDontScrub" in is currently broken. The "DontScrub" methods ensure that the actual values for DateTimes and GuIDs are used in output files. Since these values are generated every time the tests are run, not scrubbing these values means that the output changes every time.
Similar to Verify Tests, you execute the tests once to generate the verify files.
However, even after generating and setting up the verify files, the tests will fail because both tests have "Unaccounted for" namespaces. That means, namespaces exist that are neither within the proper architecture, nor listed as external or violating namespaces, the latter being technical debt to be fixed in the future. See the comments in the test files to see how to fix these tests.
For Endpoints, run or debug the Host project and open one of the two URLs:
For Testing, check out the TestArchitecture.cs file