Abstracting time

Probably one of the most common tight couplings I see is a dependency on system time, i.e. using DateTime.Now or DateTime.UtcNow directly in your code without any abstraction around it. A tight coupling that typically leads to very complex and fragile tests when trying to ad test coverage.

So just like in this excellent article I have done the journey from using an interface to abstract time to the ,more functional approach of providing a lambda instead of a full fletched interface. Two approaches that both feels like another level of indirection only for testability rather than an improvement to the design.

But injecting a time abstraction is not always the answer. As described in the article linked above sometimes the solution is not to inject the abstraction at all but rather give the date as a parameter. For convenience and in order to not have every caller deal with getting the right time you might add a single line convenience wrapper (just like in the article) to keep your code clean.

While the linked article deals with an example where you look for a time period based of "now" there is another common scenario I've come across where time is being used and that is when you want something to time out or happen after a certain period of idle time. In all these situations I have found it easier to test using TimeSpan.Zero as the length of the timeout rather than abstracting time.

For code where time actually matters, like when you are storing time stamps you still can typically get away with not actually abstracting time since you can always compare your recorded timestamp with the time before and after the call you make.

So for most of your needs you should be able to test time based code without even abstracting time using these three techniques. So while it might make you feel a little dirty since you no longer abstract time and hence have a tight coupling to system time it can still be OK. Remember an abstraction should be there because it makes things simpler, not more complex. And for time specifically there are may ways to test time related functionality without abstracting time...

No comments:

Post a Comment