Writing software in a unit testable manner is sometimes more complicated than we want it to be. The complexity of writing testable code is typically exacerbated by the architecture of fundamental components of .NET Core. When working with users that encounter these difficulties, I usually see one of three reactions; those that skip testing those methods, those that "hack" a solution to test around a limitation, and those that take the time to write truly testable code. To this point, I've started to publish a free library of utilities that helps to bridge this gap!
A Typical Example
One of the simplest examples to understand that highlights the problems mentioned above is the usage of DateTime.Now, or any other property of the DateTime object. Consider the following Factory class.
Nothing here seems at all out of the ordinary, however, when we come to try and write a unit test we will see a problem. Consider the following Unit Test.
Again, this all looks innocent enough, until we run the code. Since we are calling DateTime.Now inside of our test to get the expected, and inside the actual method, we get the value it will be different, resulting in the Assert failing. After noticing the failure, we will see the typical developer start to "solution" around the issue. You could create a Shim/Wrapper around the actual DateTime method, which would be the most appropriate, others might try to allow a reasonable offset, which would be horrible, etc.
Introducing ICG.AspNetCore.Utilities
After writing the same code in multiple projects to wrap common classes such as DateTime, File, Folder, and the like, I created a free NuGet package that contains valuable objects, that can be injected to a product allowing things to be tested easily.
Installation
Installation of these utilities is done in two simple steps; the addition of a NuGet package and registering of the objects for Dependency Injection in startup. cs.
Once this is done, it is easy to modify the code from the prior example to use the various wrapper objects provided.
Usage
Simply inject the needed wrapper into your class, the CustomerFactory from above would be updated as follows.
We have taken careful consideration to copy all Microsoft Help documentation for all methods/overloads to the user experience will be similar. However, when writing unit tests you can now properly test.
Once we have the method updated, we can now write a Unit Test that will succeed as below. The below code does expect the inclusion of the Moq library for the creation of Mock objects.
As you can see from above, we are now able to set an expected time, still using DateTime.Now, but we can control the setup of the TimeProvider to ensure that when it is called we return the value we expect. We can even further verify that our code inside of CreateCustomer calls our code.
Going Forward
This is a simple shift in programming style, but can result in substantially easier code to modify. The ICG.AspNetCore.Utilities project is 100% Open Source and my team is constantly working on the project to add more items like this. Feel free to submit suggestions either as issues or Code Contributions. It is my goal to make unit testing easier for everyone and to prevent everyone from writing the same boilerplate code day after day. I hope this helps!