Unit testing .NET REST APIs with Xunit and Moq
Learn the basics of writing unit test for .NET REST APIs
In the last article we designed a REST API in .NET 5. The REST API was designed to perform CRUD operations on the database using best practices. In this article, we would be looking at writing unit tests for the REST API we built. You might ask yourself why test the software if it works? Great question! Unit testing helps you properly debug your application and anticipate errors when they emanate whether there are from the server or the database. Have you ever thought about Google’s server going down for even 30 mins? Hmm, that could pose catastrophic and collateral damages as a lot of businesses rely on Google to function and effectively serve their customers.
Unit testing is a type of software testing where individuals test individual units or components of their application. We would be using a popular and mature unit testing suite in the .NET ecosystem, Xunit. Too much talk already! Let’s get to write unit tests for our API.
On the Solution Explorer, right click on the project to add a new test project, xUnit Test Project (.NET Core). After naming the project go add to create.
After creating the unit test project, add Moq nuget package on the Package Manager Solution. Moq helps in mocking the behaviour of actions inside the controller we intend to test.
Right click on Dependencies inside the unit project to reference the API project.
You can rename the default class that comes with the unit test project as GenreControllerTest.cs. Inside the class, we would be writing test for all the controller actions we created in the REST API project. Test for Create, Read, Update and Delete (CRUD) operations.
Inside the test class, do ensure you reference the following namespaces and others you would add as the project progresses:
- System.Collections.Genrics
- System.Linq
- Xunit
Unit test for GetAllGenre action
Our first unit test case would be GetAllGenre_NoCondition_Returns_OkObjectResult(), Our GET action retrieves all the resources in the database and sends back a 200 OK response indicative that the query operation was successful. We are writing a test to see if the action really returns a 200 OK response.
We are going to be initializing and mocking all the behavior of the action in the Arrange section. The IGenreRepository is mocked (creating an instance of the default behavior of the repository). We also configured the mock for the AutoMapper. We can then go ahead to create an instance of the GenreController. We also initialised a GenreDTO and GenreModel object. Finally, we then setup the GetGenre action which returns an ICollection of the GenreModel type.
In the Act section we write a case where our API calls on the GetAllGenre action, we make the action result an OKObjectResult.
In the Assert section we check if the status code we got from our action result is a 200 OK. If this is true the test passes.
Unit test for GetGenreById action
Our second unit test case would be GetGenreById_Returns_OkObjectResult(), our GET action returns a specific resource based on the id passed in, if this is successful it returns us a 200 OK result indicative that the action was successful.
In the Arrange section we repeat the procedures from the previous unit test.
We repeat the same for the Act section.
Same for the Assert section. If the conditions are satisfied the test passes.
Unit test for the CreateGenre action
CreateGenre_Returns_CreatedAtRoute() would be our third unit test case, our POST action makes a request to the server to create a new resource based on the content of the DTO. If this action is successful, it should return a 201 CreatedAtRoute response from the server showing the data has been created on the database.
In the Arrange section we repeat the procedures from previous unit test.
The Act section, we repeat the same procedure from previous unit test
We also repeat the same procedure on the Assert section. If the conditions are satisfied the test passes.
Unit test for the UpdateGenre action
UpdateGenre_Returns_NoContent() is our fourth unit test case, a PUT action makes a request to the server to updates part or all of the properties of the resource in the database with a specific id. If this action is successful, it should return a 204 NoContentResult response from the server indicative that the resource has been updated.
The Arrange section follows procedures from above.
The Act section follows procedures from previous unit test.
Assert section follows procedures from above. If the conditions are met the test passes.
Unit test for the PartialUpdateGenre action
PartialUpdateGenre_Returns_NoContent(), is our fifth unit test case, a PATCH action makes a request to the server to update one part of the resource in the database based on the id passed. If the action is successful, it returns a 204 NoContent response from the server indicating it has updated that part of the resource. You would have to install Microsoft.AspNetCore.JsonPatch nuget package to complete this action.
The setup for the Arrange section is the same from previous unit test.
Act section follows procedures previous unit tests.
Assert section follows procedures previous unit tests. If the conditions are met the test passes.
Unit test for the DeleteGenre action
DeleteGenre_Returns_NoContent(), is our sixth unit test case, a DELETE action makes a request to the server to delete a resource based on the id passed into the delete action. If the action is successful, it returns a 204 NoContent response from the server indicating the resource has been deleted.
The Arrange section initializes all the Mock identities and sets up the mock behavior.
Act section follows procedures from previous unit tests.
The Assert section follows procedures from previous unit tests. If the condition is met the test passes.
We can now see below that all our test passes. This shows our application is coming along well. Unit test might be tasking but it is rewarding because it helps you debug your application to know where is defective.
That will be all for now friends. After reading through this article you should be comfortable writing unit test cases. If you need more complex examples you can check my Github project.