Creating an Application from Scratch using .NET Core and Angular — Part 6
In this article we are going to see how we can create the unit tests for the Services classes in the Domain layer, using xUnit framework.
The three most commons unit test frameworks for .NET are:
- XUnit — The newest from this list. It was written by the original inventor of NUnit v2. This framework is used by the development team of .NET Core and ASP.NET.
- NUnit — A JUnit version for .NET.
- MSTest — Framework from Microsoft.
The goal of the unit test, it’s to verify each unit from the software, in isolation, to guarantee that each unit of the code is doing exactly what it should be doing.
How to organize a Unit Test
Basically, a unit test has three sections, known as ‘AAA’, which means: Arrange, Act and Assert.
In the Arrange section, it’s where we can prepare the code for our test.
In the Act section, it’s where we are going to call the code that we want to test.
In the Assert section, it’s where we are going to validate the result from the method that was called.
So the main structure of a unit test method is similar to this:
pubic async Task Example_Of_UnitTest()
// Arrange // Act// Assert
The test methods should have a clear name that reveals the intention of the test. Don’t worry if the name is too long, it’s better a long and clear name then a small and unclear name. There are many good patterns used to write the name of the unit tests, one of them that I liked and generally use, is the pattern: MethodName_ExpectedResult_WhenCondition. For example, if we want to test the GetAll method from the CategoryService, the name can be “GetAll_ShouldReturnAListOfCategories_WhenCategoriesExist”. This way, when someone reads the name of this method, it will know what is the purpose of this test. This is not a rule, but it’s good to keep in mind to always create the methods with a clear name.
Another important point is that a unit test method should only test one thing, and if the test fails, should fail for just only one reason. This does not mean that you can only have one Assert in a unit test, but keep in mind that the Asserts must be related. For example, if you have a unit test to validate the result type of your method is of type “A”, you can have one Assert to check if the result is not null, and also another Assert to check if the result is of type “A”, but you should not have in this same test method another Assert to check something that is not related to the result type.
To create the test I will also use mock, and for that, we need to install the “Moq” framework in the unit test project. Mock are objects used to simulate objects behaviour. For example, when we test a service class, we want to test only the service method. This means that we do not want to test the repository methods that are used by the service class. So how can we deal with it? Using mock. With mock we can fake the result of the repository method, and only test the service method, that it’s exactly what we want to test.
Creating the Unit Test project
Let’s start creating the first unit test project. Open the solution with Visual Studio, and create a new xUnit project and add it on the “test” folder:
Select the xUnit, add the name of the unit test project and the location of the project (remember to add inside the ‘test’ folder). First, we are going to create the project for the Domain layer:
To install Moq open the Package Manager Console, select the unit test project and execute this command (or install Moq through the NuGet Package Manager):
It’s necessary to add the reference for the Domain layer in the unit test project. To do it, right-click on Dependencies (in the unit test project) > click in Add Reference and select the BookStore.Domain:
Testing the CategoryService
Let’s start creating the first unit test class. First, we can delete the class “UnitTest1” that is automatically created with the project, and let’s create the class “CategoryServiceTests”. On this class, we have three private properties that we are going to use in the unit test methods, and on the constructor, we initialize these properties. This is the initial structure of the CategoryServiceTests class:
On line 15 and 16, we are creating a mock to use in the tests methods. This way we can simulate the return of the methods from the CategoryRepository and the BookService classes, we will do this on the “Assert” section of our tests. On line 17 we are creating an instance of the CategoryService class, which will be used to call the methods that will be used on the “Act” section of our tests.
This test class also has two private methods, that are responsible for creating the data that will be used in the tests. This way, instead of manually create the default data on the methods that we need to use this data, we can call these methods when necessary, avoiding repeating code and making the code cleaner and easier to use. For specific data then we can only manually create the objects that we need. These methods are the “CreateCategory”, which will create a single Category, and the “CreateCategoryList”, which will create a list of Categories:
Now let’s implement the unit tests on the CategoryServiceTests class.
The CategoryService class does not have any private method, it only implements the methods from the interface ICategoryService. So these are the methods that we should test:
Before we create each test, we need to look at the implementation of each method. On this first example, we are going to create the test for the GetAll method. This is the GetAll method from the CategoryService class:
For the GetAll method, we can test if:
- The method returns a list of categories if some category exists
- The method returns null if no categories exist
- The method calls the GetAll method from the repository class only one time
This is the first test method that we have:
On line 1, there is the “[Fact]” annotation. All the unit tests should have this annotation above the method name.
Note that the method name is very clear: “GetAll_ShouldReturnAListOfCategories_WhenCategoriesExist”. This is important because then we know exactly what this unit test method is testing.
On line 4, we are creating a list of categories.
On line 6 and 7, we are mocking the return of the GetAll method from the CategoryRepository class. We are faking the result, to return a list of categories that we are creating on line 4. So when the method GetAll from the CategoryService class call the GetAll method from the CategoryRepository class, the result will be the list of categories that was created on line 4.
Finally, on line 11 and 12 we have the Asserts. These asserts are checking if the result of the GetAll method (from the CategoryService class) is not null, and also if the result is of type List<Category>, which is we are expecting to be.
On the second test method for the GetAll, we want to validate if the GetAll will return null when there is no category:
On line 4 we are mocking the result of the GetAll method from the CategoryRepository class, and saying that this method should return null.
The third test method for GetAll, we want to be sure that the GetAll method from the CategoryRepository class was called only once:
On line 4 we are mocking the result of the GetAll method from the Repository class, and on line 9 we are doing the Assert to validate that the GetAll method from the CategoryRepository class was called only once.
Executing the tests
To execute the test on Visual Studio, go to “Test” and click on “Test Explorer”:
On the test explorer, it’s possible to click to run all the tests, or also run only one of them. It’s also possible to debug a test:
After the tests are executed, if everything works as expected, they will be green like in the image above.
The next method is the GetById from the Service class:
For the GetById method we can test if:
- The method returns a category when the category exists
- The method returns null when the searched category does not exist
- The method calls the GetById method from the repository class only one time
These are the implementation of the test methods for the GetById method:
Next is the Add method from the CategoryService class. On this method, we have a small validation, that is the validation to check if a category name already exists. If the category name already exists, this category should not be added:
For the Add method we can test that:
- The method adds a category when the category name does not exist
- The method does not add a category when the category name already exists
- The method calls the Add method from the repository class only one time
These are the implementation of the test methods for the Add method:
Next is the Update method from the CategoryService class. On this method there is a validation to check if already exists a category name with a different id then the updated object id:
For the Update method we can test that:
- The method updates a category with the new name when the new category name does not exist
- The method does not update a category when the updated category name already exists
- The method calls the Update method from the repository class only one time
These are the implementation of the test methods for the Update method:
Next is the Remove method from the CategoryService class. On this method, before removing a category, is validated if exist some book that uses this category:
For Remove method we can test that:
- The method removes a category when the category does not have any related book
- The method does not remove a category when the category has a related book
- The method should call the Remove method from the repository class only one time
These are the implementation of the test methods for the Remove method:
The last method that we will test in the CategoryService class is the Search method:
For the Search method we can test that:
- The method returns a list of categories when categories with searched name exist
- The method returns null when do not exists any category with the searched name
- The method should call the Search method from the repository class only one time
These are the implementation of the test methods for the Search method:
The tests methods for the BookServiceTest class follows the same steps. Since the methods are not so different than the methods on the CategoryServiceTests class, I will not add them here on this article. You can check the complete code on my GitHub:
On next articles, we are going to see how we can create the unit tests for the Controllers on the API layer and the Repository classes on the Infrastructure layer.
Thanks for reading!