How to have more fun with UnitTests in dotnet core

Farhad Nowzari
C# Programming
Published in
8 min readMar 30, 2022

I rather not to put too many lines in the intro of this post and just get to the sweet parts directly.

Photo by Priscilla Du Preez on Unsplash

But I have to confess that when you get used to do UnitTests, specially with TDD; it is so hard not to do it. It is so hard that if your manager tells you to skip the tests, you may say “If the tests go, I go” 😂. Well maybe not that far but you got the point 😉 .

For me mostly it buys more time when the product owner comes with new features. Because by just looking at the tests I know what the code does so far and I would know where this change (either a new feature or changing an existing one) would fit in.

With that being said, let’s get started.

Wait! Are you a visual learner?

well no worries. Here you go, you can go and check my videos related to this post and enjoy coding for the rest of your life 😅

Youtube Playlist — dotnet core UnitTesting (xUnit)

And before I forget, the code in this post is available on Github, in case you want to do some tests on your own.

Well that’s enough, let’s get to the fun part.

Environment

So we are going to use the following stack:

Language: C#

Framework: dotnet 6

IDE: beloved vscode 😅

Unit testing framework: xUnit

I think the only one which would need a short description would be “why xUnit?!”

Well, really?

So let me explain. xUnit isolates each test or “Fact" as it is running like an incoming request and it doesn’t depend on the other tests.

The basics you need to prepare for your tests are:

  1. Reading from appsettings.json. This is specific right now for our case, since we want to initiate a DbContextwith a connection string. Build the IConfiguration.
  2. Initiate the DbContext.

Now to read the appsettings with ConfigurationBuilder you would do the following:

Now since the test project normally does not have an appsettings.json you need to include it in your builds. To do this you need to add the following to your .csproj .

Ok, so now that we can read the configuration in our test project, let’s use it and build a DbContext .

Building the DbContext is also pretty simple but before we continue you need to reference the project you want to test. Run the command below in your test project.

dotnet add reference PATH_TO_PROJECT_UNDER_TEST

Now to build the DbContext normally I create a DbContextFactory.cs because you need some methods there.

The first thing in the factory obviously is initializing it so it would as easy as this:

In the above snippet we already have passed the Configuration to the factory from the constructor. The above code is good to be inside a private method so you can use it with the following methods.

The above methods are really important. When you want to load an entity and pass it to your SUT (System-Under-Test) those entities should no longer be tracked by any DbContext because that is how it is in real run. The data which comes to your function is coming mostly from an already processed source (UI, another service or ….) .

Note: NOT doing the above, once got me into a misunderstanding and lead into a disaster. Read the following post if you want to know about it.

So now its time to automate your migrations in UnitTests.

create a public method in your DbContextFactory.cs and put the following code there.

So the above method first will check if the Database is reachable. if it is not reachable one reason could be that it is not yet created so it would try to migrate the database. Now if the database is already there it will check if there is any new migrations to apply then if there are some it will try to run the migration.

This way if you actually also have some data on your UnitTest database, you will test the migrations too somehow.

public abstract class TestBase {}

This class will be the abstract class which all your tests will extend.

You don’t want to have the same code copy-pasted in each test class right? because you are a wise developer 😉.

So you are going to setup the TestBase like this,

So as you see the differenct between xUnit and nUnit here is that there is not Setup or TearDown . It is intelligence! So the constructor of the class is the Initiator and in case you need a TearDown you can implment IDisposable .

One thing to notice here is the Collection attribute on the TestBase class. What it does is that it will make all the tests to run Serially and not in parallel. Why we need this? well since in this case we are using a real database, if the tests run in parallel you will get into issue with CleanDatabase method. For example the Test1 and Test2 are running. Test1 is done running and Test2 is still being ran. Meanwhile Test3 is started to run and will call the CleanDatabase since it is on the constructor. Now Test2 will fail because Test3 removed it’s data.

You also can use InMemoryDatabase built into EfCore but you have to becareful that since it is not your production database, specially with some queries you may get positive results in tests but failure in prod.

Consider this simple case

dbContext.People.OrderByDesc(x => x.ModifiedOn).ToList();

The above would run fine in InMemoryDatabase but it will fail on mssql since EfCore’s mssql driver cannot translate it to a sql statement.

Ok, now that we have the Tests infrastructure in place, let’s create the first UnitTest.

First UnitTest

Let’s say that you want to create a person.

Before we continue and create a person, I want to introduce you to another library which I use on the service itself.

This is a briliant way to isolate your methods from each other.

Now let’s say you know what CQRS is and you are using Mediator to implement a command for creating a person. Let’s call it CreatePersonCommand .

Before you code this command, you gonna have a NotImplementedException on the Handler of this command and that’s enough for now to create your test.

On the UnitTest project I normally create a test class with the same name for the system under test . So we call this test classCreatePersonCommandTests .

Well if you have noticed, we are trying to do TDD or Test Driven Development . Why, you ask? Well, when you master this method, you will learn how to break down your method into smaller pieces.

It means you will have the big picture about the person creation but you will do it step by step.

  1. Create the person with given fields
  2. If the dto contains an image an ImageId should be assigned on the Person record.

So let’s start with the simple case. Let’s assume the Person entity has FirstName and LastName .

So the test would look something like:

Not much is going on. If more goes on than this, then your UnitTest gonna need UnitTest 😅.

The below library is used to do Assertions .

The part which is under test, is line 6 and this test will fail 😱. You have not yet implemented anything right? This is the point in TDD . You will right a failed test and will try to make it pass (Programming Gamified🕹️).

When you made it pass, it means that there is a high chance that your code gonna do whatever you expected.

Now let’s say you want to upload a picture and assign the ImageId to the person. We will need to mock it up 💪

Mocking methods in dotnet core

Let’s say you have tested and implemented an UploadImageCommand . Since you are a wise developer and don’t want to implement the same logic inside the CreatePersonCommand you will call this command inside CreatePersonCommand .

Now a BIG QUESTION MARK will pop.

“Since I want to now right a test to see if the ImageId will be added to the person or not should I actually run the UploadImageCommand ??!! ”

Well my friend the answer depends on your TestLevel . Do you want an IntegrationTest or a UnitTest.

Since here we are talking about UnitTest, the answer would be NO.

You need to Mock the UploadImageCommand.Send() method.

To mock, I use the following library. Pretty easy and straight forward to use with a huge community.

So now let’s create the following test ShouldHaveAssignedAnImageIdToThePerson .

So the above test would fail since it is not implemented yet.

After you implement the method you will notice that you are going to need to pass the IMediator to your test because now it won’t even build.

But here is where you need to do a Mock.

You need to mock IMediator and “pretend ”that the UploadImageCommand has ran.

To mock IMediator and just get an instance you need to do the following (I’m basically documenting the Mock library :D )

var mediator = new Mock<IMediator>().Object;

and you can pass the mediator to CreatePersonCommand . BUT the test will still fail. You need to Setup the send method.

After doing the setup, now do the following to get the object

var mediator = mediatorMock.Object;

The above snippet will setup the Send method BUT your test still will fail. The mocked method in CreatePersonCommand will return a random Guid and will set it on the PersonId but you will get an exception from EfCore and theDatabase since the ImageId doesn’t exist (Only if you have a constraint here. Otherwise you are good to go).

I would leave this to you to do the right way to mock your Mediator in a way to resolve the Constraint issue 😉

Don’t hesitate to ask questions though. But try to get creative and come up with a solution. One of the main points of doing these kind of tests is also to know how the code is functioning.

Conclusion

I think at the end I would say the main reason that people avoid UnitTesting is due to not knowing how to start. It seems like something extra to code; But I would say when you get used to do it, eventually you would code efficiently and it’s no longer about coding more or less 😉.

Have fun coding ❤️

--

--

Farhad Nowzari
C# Programming

Co-Founder and Software Architect at berrybeat GmbH.