ASP.NET Core API Unit Tests

Johan Vergeer

ASP.NET Core API Controllers are just classes, which means they can be unit tested.

This can be explained best with an example.

Person Model

We'll start off with a Person model.

This model has a Name and an Age property. It also has an IsValid property, returning whether the model is in a valid state. This last property will be used later when we will work on the Post request.

IPersonRepository

I have only created the repository interface because an implementation is not required for these tests. The methods are self-explanatory so I won't go into any detail here.

PersonController

The PersonController serves as the API endpoint. The Get() method returns all people from the repository, and the Post() method allows a new Person to be added to the repository. Note a BadRequest is returned when the IsValid property on the Person model is false. Also note the IPersonRepository interface is passed in to the constructor. I'll get back on this later when we'll look at the tests.

Tests

I'm using xUnit.Net, FluentAssertions and Moq for these tests.

Let's have a look at the tests. As I stated before, we're passing in the IPersonRepository interface to the constructor of PersonController. But we don't have an implementation for IPersonRepository. This is on purpose, sinse we want to test the controllers implementation, and not an implementation of IPersonRepository.

For this reason we'll use Moq, which is an awesome library we will use to create a mock IPersonRepository. The following code snippet shows how mock the repository is instantiated.

Get all people - empty list

For the first test we'll expect an empty list of Person. Before creating the PersonController, we have to setup the Mock object to return an empty List<Person> when FindAll() is called.

If we don't setup the mock, it will return null and throw an ArgumentNullException when ToList() is called in the controller.

After the controllers Get() method is called we assert the expected return type and whether the returned list is really empty.

Get all people - people are returned

The next test we'll expect three Person objects. The people object we created is used in the Returns() method of the repository setup. This tells Moq to return the people whenever the FindAll() method is called on IPersonRepository.

After the controllers Get() method is called we assert the expected return type again, but this time we expect three Person objects, which should be equivalent to the people object we passed into the Returns() method of the repository setup.

Now that this works, let's see how we can add a Person.

Post a person - Person has valid state

After retrieving people from the API, it would also be nice if we can add people. That will be tested next.

In this first test, which is a happy flow, the Person object will be added. Since the Add() method on IPersonRepository doesn't return anything, we don't have to do a setup. So we just Post a Peron object, after which we assert whether we response is an OkObjectResult and has a valid message. One thing we do want to verify here though, is whether the Add() method was called on IPersonRepository once and only once.

Post a person - Person has invalid state

In this last test, we have to see what happens if the Person doesn't have a valid state, which we'll test by not setting the Name property. In this case we assert whether the returned result is a BadRequestObjectResult and has a valid message. A more important assertion to note here is that we verify the Add() method on IPersonRepository is Never called.

Conclusion

As you can see, we can test an API controller without any integration to other components of the application. We didn't even need an implementation for the repository. This way we have a fast and loosely coupled way to test our controllers.

The code for this project is located in the GitHub repo.