This is the first article from the series where we are going to present different topics related to unit testing with xUnit in ASP.NET Core MVC.

We are going to start with a brief overview of the xUnit tool and our starting project.

Later on, we are going to add a new class with validation logic and finally, learn how to test that new functionality with the xUnit project.

You can download the source code for the starting project on our GitHub repository.

We have also provided the source code for the finished project for this article.

For the complete navigation of this series, you can visit ASP.NET Core MVC Testing.

These are the topics we are going to cover:

So, let’s dive right into it.

Overview of the xUnit Tool

xUnit is a free, open-source, testing tool for .NET which developers use to write tests for their applications. It is essentially a testing framework which provides a set of attributes and methods we can use to write the test code for our applications. Some of those attributes, we are going to use are:

  • [Fact] – attribute states that the method should be executed by the test runner
  • [Theory] – attribute implies that we are going to send some parameters to our testing code. So, it is similar to the [Fact] attribute, because it states that the method should be executed by the test runner, but additionally implies that we are going to send parameters to the test method
  • [InlineData] – attribute provides those parameters we are sending to the test method. If we are using the [Theory] attribute, we have to use the [InlineData] as well

As we said, xUnit provides us with a lot of assertion methods we use to validate our production code. As we progress through this series, we are going to use different assertion methods to test different production functionalities.

Once we write our test method, we need to run it to be sure whether it works or not. For that purpose, we are going to use the Visual Studio’s Test Explorer, which we can open by opening the Test menu and then Windows > Test Explorer. We can use a keyboard shortcut as well: CTRL+E, T.

Quick Overview of the Starting Project

We have created a starting project to start this series off faster. We strongly recommend downloading and using it in the rest of the series.

So, let’s just have a quick project overview:

Project Overview - Unit Testing with xUnit

We can see that we have a repository class for the repository logic with its IEmployeeRepository interface. This will be quite important for us once we start writing tests for our controller in future articles.

Our controller contains three actions, one for the GET request and two for the POST request. We can see views for Index and Create actions as well.

Finally, we can see the Migrations folder, which contains migration files for our series. So, in order for you to use the prepared data, you have to change a connection string in the appsettings.json file and just run the project. It will automatically create a database and seed all the required data.

Now, that we are familiar with the starting project, we can move onto the next phase by adding an additional class with validation logic.

Adding Validation Functionality in Our Project

Before we start, let’s take a look at our Employee entity class:

And the HttpPost action in the controller class:

In the Create action, we are adding a new employee object to the database if the model is valid. But now, we have decided to add additional validation for the AccountNumber property. Next, we need to create a new validation class and, after that, write tests for each validation rule inside that class.

So, let’s start by adding a new folder named Validation and inside it a new class AccountNumberValidation:

So, we want to ensure that the AccountNumber consists of three parts with different lengths (3, 10 and 2). Also, we want to ensure that those parts are divided by the minus sign separator.

That being said, we can see that if delimiters are invalid we are throwing an exception. If any of the AccountNumber parts is invalid, we return false. Finally, if everything goes well, we return true.

At first glance, this looks great and our validations are up to the task. But, let’s test those validation rules and make sure that everything works as expected.

Preparing the Testing Project

Let’s start by creating a new xUnit Test Project and naming it EmployeesApp.Tests:

Project creation - Unit Testing with xUnit

A new project will prepare a single test class for use, named UnitTest1.cs and will have installed xUnit library and xUnit runner as well:

xUnit library installed

We can remove UnitTest1 class, add a new folder Validation and create a new class AccountNumberValidationTests in it:

xUnit project structure - Unit Testing with xUnit

Since we want to test the validation logic from the main project, we have to add its reference to the testing.

After we have done these preparations, we are ready to write some tests.

If you like, you can add a new test project from the command window as well. All you have to do is to open your cmd window next to the main project’s solution file and type following commands:

mkdir EmployeesApp.Tests – to create a new folder

cd EmployeesApp.Tests – to navigate to the new folder

dotnet new xUnit – to create the xUnit project with the same name as the parent folder

Unit Testing with xUnit

So, let’s modify the AccountNumberValidationTests class:

We are going to use the _validation object with all the test methods in this class. Therefore, the best way is to create it in a constructor, and then just use it when we need it. By doing so, we prevent the repetition of instantiating the _validation object.

Below the constructor, we can see our first test method decorated with the [Fact] attribute. Pay attention to the naming convention we use for test methods:  

[MethodWeTest_StateUnderTest_ExpectedBehavior]

The method’s name implies that we are testing a valid account number and that the test method should return true. We can achieve that by using the Assert class and the True method which verifies that the expression inside it returns true. For the expression, we call the IsValid method from the AccountNumberValidation class and pass a valid account number.

Now we can run the Test Explorer and verify if our test passes:

FIrst test result

We can see that this test passes in the class itself:

First test result passes in the code

Works great. Let’s move on.

Theory and InlineData

In the AccountNumberValidation class, the IsValid method contains validations for the first, middle and last part of the account number. Therefore, we are going to write tests for all these situations. Let’s start with the test where the first part is wrong:

We expect our test to return false if we have a wrong account number. Therefore we are using the False() method with the provided expression. Of course, to verify this, we have to use the Test Explorer:

First account part wrong test passes

We can see that the test passes. But now, if we want to test an account number with 2 digits for the first part (we tested just with 4 digits), we would have to write the same method again just with a different account number. Obviously, this is not the best scenario. To improve that, we are going to modify this test method by removing the [Fact] attribute and adding the [Theory] and [InlineData] attributes:

Now, let’s check the result:

Theory attribute in tests

Even though we have only two test methods, the test runner runs three tests. One test for the first test method and two tests for each [InlineData] attribute.

Additional Tests

Now when we know how to use the [Theory] and [InlineData] attributes, let’s write additional tests for our account number:

There is nothing new in the code above (except different parameters), so we can run the test runner right away:

Additional tests

Excellent! One more test to go.

Testing Exceptions

In the IsValid method, we verify that both delimiters should be minus signs. If this is not the case, we throw an exception. So, let’s write a test for that:

We test three different situations here when the second delimiter is wrong, when the first delimiter is wrong, and when both of them are wrong. To test an exception, we have to use the Throws<T> method with the exception type as a T value. Note that, we are using a lambda expression inside the Throws method which is a little different from what we have used before.

Having done this, let’s check the result:

Last tests fail

Well, would you look at that! Our test has failed.

To be more precise, two of them have failed and one has passed. So this means that our validation check in the IsValid method is wrong. And now, we see why tests are so important. Even though the code looked like a good one at first glance, now we can see that it is not that good. So, let’s fix it:

Great! Now, let’s run the test again:

Last test passes

Excellent! Everything is working well.

Conclusion

So, this brings us to the end of the first article in the series.

We have learned how to create the xUnit project and how to use [Fact], [Theory] and [InlineData] attributes. Also, we have created several tests to test our validation logic from the AccountNumberValidation class. But this is just a beginning.

In the next article, we are going to learn how to test our controller class and how to use mocked objects with the testing code.

If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published .NET Core content we encourage you to subscribe to our blog.