Take advantage of the NetArchUnit to enforce architecture rules in your application and ensure consistency of quality and standards. Unit testing is how we ensure that the blocks of code we write do what we intended them to do. There are some open-source frameworks available to unit test .NET applications, namely, NUnit and xUnit.Net. You should always incorporate unit testing in your software development workflow to reduce or eliminate errors in your applications. You might also take advantage of frameworks such as ArchUnit or NetArchTest to write unit tests that can help enforce architectural rules. Inspired by ArchUnit for Java, Ben Morris’s NetArchTest is a simple framework that can be used to enforce architecture rules in .NET Framework or .NET Core as well as in .NET 6 projects. This article talks about the importance of enforcing architectural rules and how to leverage NetArchTest to achieve this. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here. The need for enforcing architectural rules There are plenty of static code analysis frameworks and tools available for checking code quality in .NET, .NET Core, or .NET 6. Two popular tools are SonarQube and NDepend, for starters. Static code analysis is also available as part of Visual Studio. However, few of these tools help you preserve the architecture design patterns or enforce architecture rules in your source code. And if you don’t regularly validate or enforce these rules, the design or architecture of your application will degrade over time. Eventually you will discover that maintaining the codebase has become a daunting task. While the static code analysis tools help you to validate or enforce generic best practices, you can take advantage of NArchTest to create unit tests that enforce the architecture rules in your .NET, .NET Core, and .NET 6 applications. These include conventions for class design, naming, and dependency in your codebases. You can use NArchTest in your unit test methods and then incorporate these test methods in the build and release pipeline so that the architecture rules are validated automatically with each check-in. Create a Unit Test project in Visual Studio 2022 First off, let’s create Unit Test project in Visual Studio 2022 using the xUnit Test Project template. Following these steps will create a new Unit Test project in Visual Studio 2022: Launch the Visual Studio 2022 IDE. Click on “Create new project.” In the “Create new project” window, select “xUnit Test Project” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Optionally check the “Place solution and project in the same directory” check box, depending on your preferences. Click Next. In the “Additional Information” window shown next, select .NET 6.0 as the target framework from the drop-down list at the top. Leave the “Authentication Type” as “None” (default). Ensure that the check boxes “Enable Docker,” “Configure for HTTPS,” and “Enable Open API Support” are unchecked as we won’t be using any of those features here. Click Create. This will create a new xUnit project in Visual Studio 2022. We’ll use this project in the subsequent sections of this article. Create a Class Library project in Visual Studio 2022 Let’s now create a class library project in Visual Studio 2022. Following these steps will create a new class library project in Visual Studio 2022: Launch the Visual Studio 2022 IDE. Click on “Create new project.” In the “Create new project” window, select “Class Library” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Click Next. In the “Additional Information” window shown next, select .NET 6.0 as the target framework from the drop-down list at the top. Click Create. This will create a new Class Library project in Visual Studio 2022. We’ll use this project in the subsequent sections of this article. Create model classes in .NET 6 Let’s assume that the name of the Class Library project is Core.Infrastructure. In the Solution Explorer window, choose this project and then click “Add -> New Folder” to add a new solution folder to the project. Models should have the same name as their solution folder. Now create a class named BaseModel inside the Models solution folder and insert the following code: public abstract class BaseModel { public int Id { get; set; } } Create two more model classes named Product and Customer. Each of these two classes should extend the BaseModel class as shown below. public class Product: BaseModel { public string Name { get; set; } public decimal Price { get; set; } } public class Customer: BaseModel { public string FirstName { get; set; } public string LastName { get; set; } } Create service classes in .NET 6 Create another solution folder in the same project and name it Services. Create an interface named IBaseService inside this solution folder and give it the following code: public interface IBaseService { public void Initialize(); } The Initialize method must be implemented by all classes that implement this interface. The ProductService and CustomerService classes implement the IBaseService interface as shown in the code snippet given below. //ProductService.cs using Core.Infrastructure.Models; namespace Core.Infrastructure.Services { public sealed class ProductService: IBaseService { public void Initialize() { //Write your implementation here } public List<Product> GetProducts() { return new List<Product>(); } } } //CustomerService.cs using Core.Infrastructure.Models; namespace Core.Infrastructure.Services { public sealed class CustomerService: IBaseService { public void Initialize() { //Write your implementation here } public List<Customer> GetCustomers() { return new List<Customer>(); } } } Note that, for the purposes of this simple implementation, the Initialize method of both the ProductService class and the CustomerService class has been left blank. You can write your own implementation for these. Install the NetArchTest.Rules NuGet package So far so good. Now add the NetArchTest.Rules NuGet package to your project. To do this, select the project in the Solution Explorer window and right-click and select “Manage NuGet Packages.” In the NuGet Package Manager window, search for the NetArchTest.Rules package and install it. Alternatively, you can install the package via the NuGet Package Manager console by entering the line shown below. PM> Install-Package NetArchTest.Rules Write architecture unit tests in .NET 6 Lastly, you should write the architecture unit tests to check if the source code under test conforms to your standards. Note that the term “standards” here is relative, and you may assume that these standards will be defined by you. The following test method verifies that your service classes have a name with a Service suffix. [Fact] public void ServiceClassesShouldHaveNameEndingWithService() { var result = Types.InCurrentDomain() .That().ResideInNamespace(("Core.Infrastructure.Services")) .And().AreClasses() .Should().HaveNameEndingWith("Service") .GetResult(); Assert.True(result.IsSuccessful); } You could have another rule that verifies that all of your service classes implement the IBaseService interface. The following test method illustrates how this can be achieved. [Fact] public void ServiceClassesShouldImplementIBaseServiceInterface() { var result = Types.InCurrentDomain() .That().ResideInNamespace(("Core.Infrastructure.Services")) .And().AreClasses() .Should().ImplementInterface(typeof(IBaseService)) .GetResult(); Assert.True(result.IsSuccessful); } You could also have a rule that verifies that the service classes are public and not sealed. If these classes are sealed, you won’t be able to extend them further. [Fact] public void ServiceClassesShouldBePublicAndNotSealed () { var result = Types.InCurrentDomain() .That().ResideInNamespace(("Core.Infrastructure.Services")) .Should().BePublic().And().NotBeSealed() .GetResult(); Assert.True(result.IsSuccessful); } When you run these test methods, you should find that all of them pass, i.e., they will be successful. Try changing the code and re-running the tests to check conformance to the rules we discussed. IDG The NetArchTest unit tests in action. Remember that in the newer versions of C# you can have a default implementation of members in an interface. So, if you have an interface that is implemented by one or more classes, you can write the default implementation in the interface. This holds true if you’re writing code that is common across all implementations of the interface. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos