top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

Writer's pictureAhmed Tarek

Unit Testing Best Practices In .NET C#

Updated: Apr 17

Tips, tricks and best practices of Unit Testing in .NET C# using NUnit and Moq.


Tips, tricks and best practices of Unit Testing in DotNet (.NET) CSharp (C#) using NUnit and Moq. Coding Code Programming Software Engineering Architecture Development Coverage Mock Stub Abstract Dependency Injection (DI) Test Driven Development (TDD)
Photo by Girl with red hat on Unsplash, adjusted by Ahmed Tarek

The Scope


In this article we are going to highlight some of the important tips and tricks to follow when using NUnit and Moq libraries to cover our .NET C# applications with Unit Tests.


You should not use this article as definitive guide to unit testing as this is not the goal or scope of this article. However, in this article you would find some of the important tips, tricks, and best practices to help you achieve the best results with your .NET C# application unit testing and coverage.


 

Tips, tricks and best practices of Unit Testing in DotNet (.NET) CSharp (C#) using NUnit and Moq. Coding Code Programming Software Engineering Architecture Development Coverage Mock Stub Abstract Dependency Injection (DI) Test Driven Development (TDD)
Frameworks Used. Photo by Alfons Morales on Unsplash, adjusted by Ahmed Tarek

Frameworks Used


Unit-testing Framework


Tips, tricks and best practices of Unit Testing in DotNet (.NET) CSharp (C#) using NUnit and Moq. Coding Code Programming Software Engineering Architecture Development Coverage Mock Stub Abstract Dependency Injection (DI) Test Driven Development (TDD)
NUnit. Image adjusted by Ahmed Tarek

We are going to use NUnit as the unit-testing framework.


You can find all the NUnit official documentations on this link.


Also, you can specifically find the documentation of all the attributes provided by NUnit on this documentation page.



Mocking Framework


Tips, tricks and best practices of Unit Testing in DotNet (.NET) CSharp (C#) using NUnit and Moq. Coding Code Programming Software Engineering Architecture Development Coverage Mock Stub Abstract Dependency Injection (DI) Test Driven Development (TDD)
Moq. Image adjusted by Ahmed Tarek

We are going to use Moq as the mocking framework. Moq is the most popular and friendly mocking framework for .NET.


If you want to have a boost on how to start using Moq, you can find a quick start guide on this link.


You can find all the Moq official documentations on this link.


 


 

Tips, tricks and best practices of Unit Testing in DotNet (.NET) CSharp (C#) using NUnit and Moq. Coding Code Programming Software Engineering Architecture Development Coverage Mock Stub Abstract Dependency Injection (DI) Test Driven Development (TDD)
Tips and Hints. Photo by Nick Fewings on Unsplash, adjusted by Ahmed Tarek

Tips and Hints


In this section we are going to discuss some tips and hints which would be useful while writing our unit tests.


 

Setting Up and Tearing Down


When we are writing our unit tests, we usually have some preparations and cleaning up code to be run before and after our unit tests.


NUnit provides us with some attributes to be used for these preparations and cleaning up tasks.



OneTimeSetup: is used to set the code to be executed only once before the first test.


SetUp: is used to set the code to be executed before every test.


TearDown: is used to set the code to be executed after every test.


OneTimeTearDown: is used to set the code to be executed only once after the last test.


If we have like 2 tests, the order of execution would bs as follows:

  1. OneTimeSetup ▶ Before Test #1

  2. SetUp ▶Before Test #1

  3. TearDown ▶After Test #1

  4. SetUp ▶Before Test #2

  5. TearDown ▶After Test #2

  6. OneTimeTearDown ▶ After Test #2


 

Test Cases


When we are writing our unit tests we might need to cover more than one test case in the form of input and output pairs.


Therefore, instead of writing more than one single test for each of these test cases, we can use the TestCase attribute provided by NUnit.


The TestCase attribute could be used in more than one format.


For example, you can use it this way:



Or you can use it this way:



Or even in more ways. You can always check the official documentation.


 

Test Cases From a Source


Sometimes we need to share the same test cases between more than one test. In this kind of situations using TestCase attribute would not be enough.


Therefore, NUnit is providing us with the TestCaseSource attribute so that we can set our cases into a shared module and then start using it with different tests.


This is one of the ways we can use the TestCaseSource attribute:



If you want to explore the other ways, you can always check the official documentations.


 

Multiple Values


Sometimes we need to use more than one value as an input for our test. In this case it would not be good to just duplicate the test for each value.


You might think of using the TestCase attribute and it would work. However, it would be an overkill.


Additionally, what if you have more than one input and each of these inputs could be one of a list of options. You might need to add a TestCase attribute for each combination. This would be a total mess.


Instead of doing all of this, NUnit is providing us with the Values attribute.


For example, if we use it as follows:



This means that the MyTest test would be executed 6 times as follows:

MyTest(1, "A")
MyTest(1, "B")
MyTest(2, "A")
MyTest(2, "B")
MyTest(3, "A")
MyTest(3, "B")

See, using the Values attribute could save us the hassle of explicitly writing all these cases.


 

A Range


What if we need to do the same but without providing the full list of values? We only want to provide the first value, last value, and the step to move with.


For this kind of cases, NUnit is providing us with the Range attribute.


For example, if we use it as follows:



This means that the MyTest test would be executed 4 times as follows:

MyTest(0.2)
MyTest(0.4)
MyTest(0.6)
MyTest(0.8)

See, using the Range attribute could save us the hassle of explicitly writing all these cases.


Worth to mention here that both the Values and Range attributes can be combined as follows:



This means that the MyTest test would be executed 9 times as follows:

MyTest(1, 0.2)
MyTest(1, 0.4)
MyTest(1, 0.6)
MyTest(2, 0.2)
MyTest(2, 0.4)
MyTest(2, 0.6)
MyTest(3, 0.2)
MyTest(3, 0.4)
MyTest(3, 0.6)

Nice, right?


 

Repeating


Sometimes we find ourselves in the need to repeat running a unit test many times in a continuous manner. Most of the cases we need to do this to test the performance impact or for stress testing some code.


Me myself, in more than one occasion I needed to do this because we were having a unit test which “sometimes” fails on the build server. I suspected that this was caused by some racing which happens or some resources which are not fully controlled.


Therefore, in this case, I used the Repeat attribute that NUnit is providing us with.


For example, if we use it as follows:



This would cause the MyTest test to run for 25 times in a row.


Using the Repeat attribute makes it too easy to detect wobbly tests and find the failure root cause.


 


 

Collection Asserts


Sometimes we need to do some asserts on collections. In this case, NUnit is providing us with some useful utilities to use.


Assert.IsEmpty: may be used to test either a string or a collection or IEnumerable. When used with a string, it succeeds if the string is the empty string. When used with a collection, it succeeds if the collection is empty.


Assert.IsNotEmpty: may be used to test either a string or a collection or IEnumerable. When used with a string, it succeeds if the string is not the empty string. When used with a collection, it succeeds if the collection is not empty.


Assert.Contains: is used to test whether an object is contained in a collection or not.

CollectionAssert.AllItemsAreInstancesOfType: is used to test whether all items in a collection are instances of a certain type or not.


CollectionAssert.AllItemsAreNotNull: is used to test whether all items in a collection are not null or not.


CollectionAssert.AllItemsAreUnique: is used to test whether all items in a collection are unique or not.


CollectionAssert.AreEqual: is used to test whether each item in a collection is equal to its corresponding one in another collection. Items are compared according to their order in both collections.


CollectionAssert.AreEquivalent: is used to test whether each item in a collection is equal to a corresponding one in another collection. Items are compared regardless their orders are.


CollectionAssert.AreNotEqual: is the opposite of CollectionAssert.AreEqual.


CollectionAssert.AreNotEquivalent: is the opposite of CollectionAssert.AreEquivalent.


CollectionAssert.DoesNotContain: is used to test whether a collection of items doesn’t contain a certain object or not.


CollectionAssert.IsSubsetOf: is used to test whether a collection of items is a subset of another collection or not.


CollectionAssert.IsNotSubsetOf: is used to test whether a collection of items is a not a subset of another collection or not.


CollectionAssert.IsOrdered: is used to test whether a collection of items is ordered or not.


For any further details, you can always check the official documentations.


 

Event Handlers


Sometimes you need to test some of your modules which handle events fired by other modules.


In these cases, the best way to handle this is to have the modules which fire the events abstracted so that you can mock them for testing. Let me show you an example.


Let’s say that we have a sensor which reads the acceleration of a car or any object.



As you can see, it is just a simple interface with an event. Although we don’t actually need an implementation for the sake of our example, I just provided a simple one for brevity.


Now, let’s say that we have an AccelerationDashboard module which uses the IAccelerationSensor module to always get the updated acceleration value.



As you can see, our AccelerationDashboard class depends on the IAccelerationSensor module and expects it to be injected through the constructor.


Also, it just handle the AccelerationChanged event raised by the IAccelerationSensor module.


Now, for testing the AccelerationDashboard module, this is what we can do:



See, it is that easy. Using Moq we can mock the IAccelerationSensor module and trigger the AccelerationChanged event in order to then assert the Acceleration value on the AccelerationDashboard module.


 


 

Timers


One of the challenges you might face when your system works with Timers is how to fully cover your system with unit tests.


To do so, you will need to abstract the Timer class and do some tweaks to achieve your goals.

I had already published a fully detailed article about this. If you are interested into the topic, you can check my article Best Practice for Using Timers in .NET C#.


 

DateTime


Also, if your system uses DateTime or DateTimeOffset, you will eventually face some challenges to fully cover your modules with unit tests.


To conquer these challenges, you will need to abstract these classes into providers, define them as dependencies and then you would be able to mock and stub them as needed.


I had already published a fully detailed article about this. If you are interested into the topic, you can check my article DateTime Best Practices In .NET C#.


 

I/O File and Folder Operations