Although i know already a lot has been written about this dilemma, i never saw a good and descriptive enough solution to this problem. Let’s first start by situating the problem with a simple code example.
So how would I be able to test the fact that when I create a new order, the CreatedDate is filled in with the value of DateTime.Now.
The only thing i could test for is if the CreatedDate is not null. But that would be a lousy test because the code in the Order constructor could also be setting the CreatedDate to a given date (01/01/1830).
Proposed Solution 1
Creating an interface (for instance ISystemClock) with off course the implementation
Although it allows the dependency to be mocked in unit tests, it would make the Order code look like this, which feels a little strange
Proposed Solution 2
Wrap the ISystemClock into a static class which allows changing the implementation:
Which would result in the following, much nicer Order constructor:
This is beginning to get more like it, it allows mocking the value of ISystemClock.Now trough setting the implementation via the Set method. But this could get you into a whole lot of trouble of you didn’t put the right test initialization code into your tests to reset the SystemClock underlying implementation class back to RealSystemClock.
What about if our test runner allowed multithreaded testing, 1 test could affect another.
What if someone changed it in your real code, I know developers are not morons, but still the concern remains and it seems to be a whole lot of code to just mock out DateTime.Now.
Proposed Solution 3
Expose DateTime.Now as an instance of a function, that can be replaced in tests , this is Ayende’s solution:
Although i like the simplicity of this solution, this is just a simplification of the code in Solution 2. I think most of the remarks of solution 2 also counts for this beauty, which can also be seen in the comments on Ayende’s blog.
Proposed Solution 4
Let’s alter Ayende’s solution and tackle the first problem: Thread Safety
Ok, this seems to solve the problem, but it removed the ability to mock our DateTime.Now. Let’s solve this by adding a method that returns a DisposableAction, that could be used in our testing.
As the SystemClock.Frozen method returns a IDisposable, it would auto-cleanup during testing and allows a very discriptive way of testing
Although this solution is still not idiot proof, it guides us to the correct use of the SystemClock’s Frozen method.
Now it’s up to you to pick your own poison!