Implementing a Testing API with .NET

Wednesday, September 22, 2021

In the first post about the basics of the Testing API, I talked about how a Testing API can help you decrease the structural coupling between your production code and your test code. In the long run, it will save you much time! A bold claim, of course, and it sounds very theoretical. To provide you with more depth than just a basic concept, I want to show you today how you can implement a Testing API with .NET. Therefore I want to look at the following aspects in more detail today:

  • How to Integrate the Testing API into a given architecture?
  • How to implement the Testing API with .NET?
  • How to structure your Test project?

If you need more information about the basics of a Testing API and what it's good for, check out my first post of this series!

How to integrate the Testing API into a given architecture?

In my first post, I mentioned that the origins of the Testing API come from the book "Clean Architecture" by Robert C. Martin. He describes this Testing API as follows:

API that the tests can verify all the business rules. This API should have superpowers that allow the tests to avoid security constraints, bypass expensive resources (such as databases), and force the system into particular testable states. This API will be a superset of the suite of interactors and interface adapters that are used by the user interface.

So ultimately, your tests call the API, and it provides everything the test needs to verify the business rule.

The main question is, if you don't have a "Clean Architecture," is it even possible to integrate the Testing API? For that, let's look at layered basic architecture.

A basic layered architecture with tests

The above architecture contains four components: the UI, the Database, the business logic, and the tests. Usually, you want to test your business logic by directly creating concrete instances of classes from the business logic. The concrete instantiation, of course, leads to tight structural coupling between your production code and your test code.

Thus, the question is, how would you fit a Testing API into this architecture? Why not simply integrating another component between your business logic and your tests?

A Testing API project fits in every architecture

You might already know this picture from my first article about the Testing API. The approach is still valid, however. The Testing API contains everything that you need to verify your Business Rules. Your Tests, no matter what kind of tests, only access your API. In the end, the Testing API component gets all the necessary information from the business logic.

So you might say: Okay, we already know that, but this is only a theory. So let's see how you can implement your Testing API with .NET.

How to implement the Testing API with .NET?

Before we look at the implementation, imagine creating an app for a car repair shop. The software has the following requirements:

  • Save customer appointments
  • Send notifications to customer
  • Provide monitoring information via a UI

We can easily use these requirements for our Testing API. I created a new .NET class library for our Testing API and added three classes to it:

  • CustomerAppointment
  • CustomerNotification
  • Monitoring

Let's assume we've already implemented the production code and some tests. You can find the source code on GitHub, so you can easily follow along with this article.

Eventually, our .NET solution for our car repair shop app looks like this:

The Testing API project inside the CarRepair solution

The CarRepair.Core and CarRepair.Application project contains all the entities, aggregates, and, in general, the application business logic. The CarRepair.Test project consumes the information only from the CarRepair.TestingApi project. Therefore our tests' structures are independent of the design of the production code. The independence gives us higher flexibility when refactoring our code.

Dependency diagram of the CarRepair solution

The above dependency diagram shows you what the main dependency paths are. The bigger the blue arrows are, the more they depend on another component. As you can see, our tests mainly rely on the Testing API, and our API only depends on the application business logic. So your Test project does not directly connect to your Application project, and thus they are structurally more independent.

Let's write one test to consume our Monitoring class from the Testing API.


The Test class uses the MSTest framework, and our test is relatively simple. We want to get the correct repair status information if the car shop updates the repair status. Instead of building a concrete instance of a class that sends the update, we create a new instance of our Monitoring Testing API.


The Monitoring class provides methods which we need to verify our business rules. The Notify method creates a new instance of an observer in those methods, which sends the latest status update. So if something changes to the structure of the RepairStatusObserver class, we don't need to worry about that in our tests, significantly if the business rules don't vary.

The GetCurrentRepairState() method needs to do a little bit more to return the current RepairState object. At first, it authenticates the user, creating an instance of the MonitoringNotificationReader for reading the new monitoring status. Then, a parser parses the description of the monitoring status into the RepairState. The Monitoring Testing API effectively encapsulates everything for us, and it hides the internal structure. If anything changes to the authentication process or how we get the RepairState object, we only need to adjust the code inside the Testing API. As long as your business rules stay the same, your tests don't change. It is as simple as that!


How to structure your Test project?

In general, there are no rules which tell you how to structure your Test project or even your Testing API project. Everything is very flexible, and you can structure it in every way you prefer. You can also have multiple Test projects, but it is easier to have only one Testing API project.

So how could the structure of your Test project look?

The structure within the Test project

One way is to have one Test project, and it contains folders for every kind of test. This folder can have, of course, further subfolders, depending on your domains and your application business logic.

As already mentioned in my first blog article, they can also leverage the Testing API if you write Unit Tests. But it blows up your API. Usually, you write Unit Tests to verify the logic inside public methods (a unit). Therefore, in this case, it makes sense to access the Application project directly. Consequently, it is better to separate your concerns to create another Test project just for your Unit Tests. You can keep all the other tests, like Integration Tests, System Tests, or even End-to-End Tests in the Test project, as we see in the above picture.


Implementing a Testing API in .NET is pretty simple. Leveraging the power of this concept takes more effort and thought into designing the API. But as you have seen in the above example, if implemented correctly once, the Testing API helps most of your test code be independent of the structure of your production code.

Here are the key takeaways:

  • The easiest way to integrate a Testing API into your architecture is by simply adding a new TestingAPI project.
  • Try to create one class for each domain area.
  • Every class inside the Testing API project can contain everything needed for the test to verify a business rule.
  • If you already have tests, try to redirect them towards the Testing API step-by-step.
  • If you still have Unit Tests, move them into a separate Test project. You have one project that consumes the Testing API and one that directly accesses the application logic via Unit Tests.

What do you think about this approach? I'm curious to see what you think about writing Testing APIs with .NET. So leave a comment down below and let me know if you liked this article.

Let us know what you
think about this article