(EN) xUnit Test with .Net Core (In-Memory DB, Swagger & JWT Authentication)

Kemal AKCIL
8 min readJan 30, 2021

Bu makalenin yazmış olduğum Türkçe versiyonuna bu linkten ulaşabilirsiniz.

You can access the whole project at https://github.com/Kakcil/xunit-test-sample-dotnet-core.

presentation

İntroduction:

In this article, we will examine how to perform advanced testing with xUnit in .Net Core (3.1) projects. We have relational tables (country, city, district) and user table for authentication. We will keep these tables in the in-memory database and statically initialize our data within the project. I will try to explain step by step through the ready project.

Swagger is added in the project. Let’s run our Service project, log in first over Swagger and get a Bearer Token. (Let’s assume that the token we received is 12345abcdef) We click the Authorize button at the top right of the page

and enter “Bearer 12345abcdef” in the modal that opens, and we can see if our service project gives the desired output before testing.

Also, the “sample-service” that uses the in-memory database is not related to xUnit. We used this alternative instead of the actual server/database to improve the functionality of the project. Under normal circumstances, only our test project should use an in-memory database. The db of the project it is testing is not a concern for xUnit.
Lets start:

Section 1: About xUnit

xUnit.net is a free, open source, community-focused unit testing tool for the .NET Framework. Written by the original inventor of NUnit v2, xUnit.net is the latest technology for unit testing C#, F#, VB.NET and other .NET languages. xUnit.net works with ReSharper, CodeRush, TestDriven.NET and Xamarin. It is part of the .NET Foundation, and operates under their code of conduct. It is licensed under Apache 2 (an OSI approved license).

We will do a functional test with XUnit. Functional testing involves checking and testing every function of the software to ensure the expected results are reached.

Software Test Types

I have created such a hierarchy table. I didn’t list the other table members because they were out off research.

.Net Unit Test Types

As you can see in the hierarchy chart above, there are 3 Unit Test types that we basically use in .Net.
The reason we use XUnit is because we think it has an advantage over others. These advantages will not be covered in this article, you can research.

What is AAA Pattern?

This Pattern, which takes its name from the substances I will define, has become a standard in the sector. It proposes that we divide our test into three parts respectively: Arrange, Act and Assert.
Each process is responsible only for the part they name.

Arrange: Organizing and preparing all necessary prerequisites and inputs.
Act: To activate the behavior in accordance with the test scenario in the class.
Assert: To verify if the expected result is realized.

What is Mocking?

This is another concept to know. An object under test may have dependencies with many objects. To isolate these dependencies, it is necessary to replace some real objects with (isolated) objects that imitate their behavior.
For example, let’s examine the scenario “We had to use a log sending service while testing a method”:
By mocking this log service, we create a mock service that looks like it but does not log and we use it in the testing process.

What are the xUnit Attributes?

[Fact] : It states that the method we wrote must be executed by the test runner and that we will not send any parameters to the test method.
[Theory] : This means we will be passing some parameters to our test code. Basically, we can send parameters with 3 methods that I will explain below.
[Theory, InlineData] : If our method to be tested takes string, int etc. type values as parameters instead of a class, we use this attribute.
[Theory, ClassData] : If our method to be tested takes a class as a parameter, we use this attribute.
[Theory, MemberData] : If our method to be tested takes string, int etc. type values as parameters instead of a class, we can use this attribute. It can be used instead of InlineData in multiple data testing operations. It is not essential.

What is the Fixture?

We will have a folder named “Fixture” inside our test project. We create it. In this “Fixture”, we will perform the operations that will connect our test project and our project to be tested. Our sample-service project includes services and we will link these services together with the processes in the “ServiceFixture.cs” file under the “Fixture” folder.
In addition, we will initialize the data to the database where we created the mock. The data we will add manually to the db of our test project should be very close to the project we will test, and if possible, exactly.

Section 2: Project Setup and Folder Structure

Setup:

Step 1
Step 2

We create our test project in 2 steps.

In order to establish the connections between our test project and the project we will test, we first add “Project Reference” to our test project as below.

Project Reference

Folder Structure:

sample-service.csproj

In the “sample-service” project:

  • We have Data Transfer Objects (Dtos) that we created as custom, which we can think of like ViewModel.
  • In our “Entities” folder, we have our DB Models and “DbInitializer.cs” file where we initialize data statically. We also have database settings in our “SampleDbContext.cs” file.
  • In the “Helpers” folder, there are methods and properties we need in the project.
  • In the “Services” folder, there are the services we use in our Controllers.
sample-service-test.csproj

In the “sample-service-test” project:

  • We are merging with services and database in the "Fixture" folder. We will perform the operations of the controllers about fixture in the classes (…ControllerTest.cs) where we wrote the tests.
  • As we can see, our naming convention is ControllerNameControllerTest.cs. It is not the rule, but it can be taken as an example.
MockAuthorize.cs
  • In the “Mock” folder, we are doing the mocking operations described at the beginning of the article.
    For example: We perform JWT Authentication operations in the MockAuthorize.cs file. Thus, while our tests pass our Controllers in the sample-service project, they will be able to access every place that has the “[Authorize]” condition.

Section 3: Testing Procedures

Let’s take a look at our CityControllerTest.cs file as an example:

CityControllerTest.cs

We inherit our class named “CityControllerTest” from “IClassFixture <ServiceFixture>”.
We produce a property named “CityController” in this class from the “CityController” type in our “sample-service” project.
Then, in our constructor function, we call this property “new CityController (fixture.CityService)” and send the desired CityService parameter in the constructor function of the CityController in the “sample-service” project from TestController. This “CityService” parameter is the “CityService” that we defined in the ServiceFixture.cs file in our test project.

Just below it, we perform our JWT Authentication process with the CityController.Authorize() call.

We have performed our Fixture procedures and test operations can now be written within our class.

I use the
MethodNameControllerName_InWhichStateToTest_ExpectedValue
naming convention in my experience. The main reason why we subject naming to a rule is: To understand which of our tests were successfully or failed when we run our tests. You can expand your research and choose the naming rule that best suits you.

test explorer

You can examine in detail how the tests work, by downloading the sample project from https://github.com/Kakcil/xunit-test-sample-dotnet-core and debugging it with breakpoints from the part shown in the picture below. You can configure it according to your own project.

debugging test

Code Coverage:

It shows how much of the code written has been tested.
Use “coverlet.msbuild” package from Nuget. This package should be added to the test project. Then in “Package Manager Console” to see the coverage rate

dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

command should be run.

coverage result

I care about Lines Coverage in my projects. I aim to keep this rate at least 60%. As you can see it fails with 41%.

So how can we increase this rate?
If we get a test report where we can see where our tests have passed the code, we can increase it by writing tests for codes that have not passed the test.
So let’s get a report. We can get this report with a command.
After running the command in “Package Manager Console”,

dotnet test --collect:"Code Coverage"

a folder named “TestResults” will be created in our test project. When we try to open the report, we will probably get the following error if we are doing this in vs2019.

error coverage

To resolve this error, https://gist.github.com/Kakcil/34d2b5d4e7bc0fb25788ec459d4f0bc5
you can examine the solution from.

coverage report

When we open the report after the error is resolved, coverage rates and details will appear. It can continue to be detailed in the results. But these rates are Blocks Coverage rates. On the Code Coverage Results window, we can right click and click “add/remove columns” after that add Lines Coverage columns from the window that opens, then Lines Coverage rates will show.

Extra:

[Theory]
[ClassData(typeof(CityControllerTheory.DetailCityOkParams))]

It is the same for both uses.

[Theory, ClassData(typeof(CityControllerTheory.DetailCityOkParams))]

Thank you for your time. I hope this has been useful.

Source: My information.

--

--