Problem
I've been teaching myself more about .Net Workflow Foundation (WF). One thing I've been working on recently is how to unit test long running workflows. I have a few examples of workflows that delay for days. I'd love to be able to advance the clock and simulate what will happen when time advances past the delay without the wait. There's a number of techniques out there that I have found. They fall into one of several camps.- Roll your own Delay Activity
- Use the custom Delay Activity from Microsoft.Activities.UnitTesting
- Runtime XAMLInjection of modified Delay Timespan
None of these appeal to me. I prefer the stock Delay Activity -- other than unit testing I have no real reason for modified behavior. Runtime injection seems a bit of a kludge. I prefer to leave the logic of the workflow intact and only modify the dependencies from the outside. In short, I have the same concern that is raised in this StackOverflow question about replacing the DurableTimerExtension.
I have the same interest as the original poster of this question. How do I inject my own mocked variation of DateTime.Now()? There are simlar, non-workflow implementations for Mocking DateTime.Now() and UnitTesting DateTime.Now(), just not a best practice for WF.
Here is one way to do it.
[Disclamer: The code I have below uses the Microsoft.Activities.UnitTesting extensions. This technique wouldn't require that framework -- but its handy enough. So I use it.]
Approach
On instances of WorkFlowApplication and WorkflowServiceHost there is an Extensions collection. In WF, this is way to register workflow extensions like Tracking, PersistenceParticipants, and services that your custom Activities can consume. One of the methods on this collection is AddCode @ Pastebin
Conclusion
This isn't quite the ideal in my mind. I'm intercepting the call that the Delay Activity (and anything else that wants to use the TimerExtension) makes to schedule a timer to fire and modifying the actual timespan that is used to schedule the timer. Any workflows that attempt to reconcile time inside and outside of the workflow will have side effects... but, well, that's kinda the point.I'd much rather intercept a request to an imaginary service from the workflow for "What time is it now?". A default (and production implementation would reply "Why its DateTime.Now(), of course [or is that UTCNow()..], A mocked implementation would reply "Its DateTime.Now().Add(new Timespan(0,15,0)) [15 minutes in the future for the less initiated].
All of these techniques have their warts. In any case, what I have written up is the closest to satifying my needs -- so I decided to write up a post for the benefit of all -- including future me. I'd love to hear of other successful approaches you might have taken in the comments.