My book contains a section on the Ambient Context pattern that uses a TimeProvider as an example. It’s used like this:
this.closedAt = TimeProvider.Current.UtcNow;
Yesterday I was TDDing a state machine that consumes TimeProvider and needed to freeze and advance time at different places in the test. Always on the lookout for making unit tests more readable, I decided to have a little fun with literal extensions and TimeProvider. I ended up with this test:
// Fixture setup
var fixture = new WcfFixture();
DateTime.Now.Freeze();
fixture.Register(1.Minutes());
var sut = fixture.CreateAnonymous<CircuitBreaker>();
sut.PutInOpenState();
2.Minutes().Pass();
// Exercise system
sut.Guard();
// Verify outcome
Assert.IsInstanceOfType(sut.State,
typeof(HalfOpenCircuitState));
// Teardown
There are several items of note. Imagine that we can freeze time!
With the TimeProvider and an extension method, we can:
internal static void Freeze(this DateTime dt)
{
var timeProviderStub = new Mock<TimeProvider>();
timeProviderStub.SetupGet(tp => tp.UtcNow).Returns(dt);
TimeProvider.Current = timeProviderStub.Object;
}
This effectively sets up the TimeProvider to always return the same time.
Later in the test I state that 2 minutes pass:
I particularly like the grammatically correct English. This is accomplished with a combination of a literal extension and changing the state of TimeProvider.
First, the literal extension:
internal static TimeSpan Minutes(this int m)
return TimeSpan.FromMinutes(m);
Given the TimeSpan returned from the Minutes method, I can now invoke the Pass extension method:
internal static void Pass(this TimeSpan ts)
var previousTime = TimeProvider.Current.UtcNow;
(previousTime + ts).Freeze();
Note that I just add the TimeSpan to the current time and invoke the Freeze extension method with the new value.
Last, but not least, I should point out that the PutInOpenState method isn’t some smelly test-specific method on the SUT, but rather yet another extension method.
Remember Me
a@href@title, b, em, i, strike, strong
Page rendered at Saturday, February 04, 2012 10:20:18 PM (Romance Standard Time, UTC+01:00)
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.