Tips, tricks and best practices of Unit Testing in .NET C# using NUnit and Moq.
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.
Frameworks Used
Unit-testing Framework
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
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 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:
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.
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
This is one of the famous challenges that many developers face when their systems heavily depend on I/O file and folder operations.
To fully cover such systems with unit tests the developer must think beyond his near obvious needs. The system design itself must be adapted into some abstracted layers where each layer has a well defined rule in the whole system.
I had already published a fully detailed article about this. If you are interested into the topic, you can check my article How To Fully Cover I/O File Based Applications in .NET C# With Unit Tests.
Console Systems
When dealing with console systems, it becomes so hard to cover the whole system with unit tests when our modules directly depend on System.Console.
That’s why we first need to abstract the console Api and start using them as dependencies. Doing this, it would be much easier to cover our console systems with unit tests.
I had already published a fully detailed article about this. If you are interested into the topic, you can check my article How to Fully Cover .NET C# Console Application With Unit Tests.
Final Thoughts
In this article we discussed some of the important tips, tricks, and best practices to follow when using NUnit and Moq libraries to cover our .NET C# applications with Unit Tests.
My advice to you is to do your own research and try to find other resources which might help you understand more about testing techniques and related best practices.
Finally, I hope you found reading this article as interesting as I found writing it.
Comments