I’ve recently received an email about a code snippet I’ve provided in Ayende’s post. Ayende, as usual, provided a simple yet good code for this problem. Mine is more complex way of doing this, but i like the pattern behind it.
You should be aware that there is no way to make DateTime.Now stop(if you don’t want to touch system clock and run your test in virtual environment, of course). You have to abstract it.
public class TimeAbstractionTests
{
public TimeAbstractionTests()
{
CallContext.SetData(Constants.CONTEXT_STORE_KEY,null);
}
[Fact]
public void Time_should_set_to_System_time_for_first_time()
{
DateTime x = Time.Now;
Assert.Equal(DateTime.Now, Time.Now);
}
[Fact]
public void Time_set_to_system_time_when_only_system_time_is_selected()
{
using(Clock.System())
{
Assert.Equal(DateTime.Now, Time.Now);
}
}
[Fact]
public void Time_should_freeze_when_frozen_time_is_selected()
{
DateTime frozenTime=new DateTime(2011,11,11);
using (Clock.Frozen(frozenTime))
{
Assert.Equal(frozenTime, Time.Now);
}
}
[Fact]
public void Time_should_nest()
{
DateTime frozenTime = new DateTime(2011, 11, 11);
using (Clock.Frozen(frozenTime))
{
Assert.Equal(frozenTime, Time.Now);
using (Clock.System())
{
Assert.Equal(DateTime.Now, Time.Now);
using (Clock.Frozen(frozenTime))
{
Assert.Equal(frozenTime, Time.Now);
}
}
}
}
}
This tells what I’d like to achieve pretty well. I want one FrozenTime, one System time, and I want them to nest(no specific reason actually)
For nesting, Stack<T> structure should be used. Everytime I ask for a Clock, it puts the demanded Time into stack stored in CallContext, and Time.Now thing takes the latest ITime instance from that stack.
Implementation for FrozenTime and DefaultTime is simple, here they are.
public class FrozenTime:ITime
{
public FrozenTime(DateTime now)
{
this.now = now;
}
private readonly DateTime now;
#region ITime Members
public DateTime Now
{
get { return now; }
}
#endregion
}public class DefaultTime:ITime
{
#region ITime Members
public DateTime Now
{
get { return DateTime.Now; }
}
#endregion
}
Now it comes to the Time class. As I said, it gets the topmost ITime(using Peek(), since we don’t want to remove) instance from the stack and invokes .Now on it.
public static class Time
{
public static DateTime Now
{
get
{
Stack<ITime> timeStack =
(Stack<ITime>) CallContext.GetData(Constants.CONTEXT_STORE_KEY);
if (timeStack == null||timeStack.Count==0)
{
Clock.System();
timeStack =
(Stack<ITime>) CallContext.GetData(Constants.CONTEXT_STORE_KEY);
}
return timeStack.Peek().Now;
}
}
}
Clock class is where I say that I want xxxxTime to be active during the scope. In order to support using(Clock.System()) like usage, I must have something IDisposable. I would have implemented IDisposable for ITime implementations, but it violates SRP so I choose another way.
public class Clock
{
public static IDisposable System()
{
var time = new DefaultTime();
PushIntoStack(time);
return new DisposeCallback(PopFromStack);
}
. . . . . .
}
As you see, when somebody calls .Dispose() in callback(which is done at the end of using() structure) it pops the latest ITime instance.
This is definitely more work than Ayende’s solution, but I like this way more.
You can download the solution here(Time.rar (35.61 kb))
testing, abstraction