In Part 2 of this four part series you learned how to use a class property to change the code’s dependency on the system clock to make the code easier to test. Adding the Now property is effective, however, adding a new property to every class isn’t always the best solution.
I don’t remember exactly when I first encountered the IClock interface. I do remember having to deal with the testability challenges of the system clock about 5 years ago. I was developing a scheduling module and needed to write tests that verified the code’s correctness. I think I learned about the IClock interface when I researched the MbUnit testing framework. At some point I read about IDateTime in Ben Hall’s blog or this article in ASP Alliance. I also read about FreezeClock in Ben’s post on xUnit.net extensions. Over time I collected the ideas and background that underlie this and similar approaches.
Fake Time 3: Inject The IClock Interface
I usually create a straightforward IClock interface within some utility or common assembly of the system. It becomes a low-level primitive of the system. In this post, I simplify the IClock interface just to keep the focus on the primary concept. Below I provide links to more detailed and elaborate designs. Without further ado, here is the basic IClock interface:
using System;
namespace Lender.Slos.Utilities.Clock
{
public interface IClock
{
DateTime Now { get; }
}
}
By using the IClock interface, the code in our example class is modified so that it has a dependency on the system clock through a new constructor parameter. Here is the rewritten code-under-test:
using System;
using Lender.Slos.Utilities.Clock;
using Lender.Slos.Utilities.Configuration;
namespace Lender.Slos.Financial
{
public class ModificationWindow
{
private readonly IClock _clock;
private readonly IModificationWindowSettings _settings;
public ModificationWindow(
IClock clock,
IModificationWindowSettings settings)
{
_clock = clock;
_settings = settings;
}
public bool Allowed()
{
var now = _clock.Now;
// Start date's month & day come from settings
var startDate = new DateTime(
now.Year,
_settings.StartMonth,
_settings.StartDay);
// End date is 1 month after the start date
var endDate = startDate.AddMonths(1);
if (now >= startDate &&
now < endDate)
{
return true;
}
return false;
}
}
}
Under non-test circumstances, the SystemClock class, which implements the IClock interface, is passed through the constructor. A very simple SystemClock class looks like this:
using System;
namespace Lender.Slos.Utilities.Clock
{
public class SystemClock : IClock
{
public DateTime Now
{
get { return DateTime.Now; }
}
}
}
For those of you who are using an IoC container, it should be clear how the appropriate implementation is injected into the constructor when this class is instantiated. I recommend you use constructor DI when using the IClock interface approach. For those following a Factory pattern, the factory class ought to supply a SystemClock instance when the factory method is called. If you’re not loosely coupling your dependencies (you ought to be) then you need to add another constructor that instantiates a new SystemClock, kind of like this:
public ModificationWindow(IModificationWindowSettings settings)
: this(new SystemClock(), settings)
{
}
In this post, we are most concerned about improving the testability of the code-under-test. The revised test method sets up the IClock.Now property so as to return currentTime as its value. This, in effect, fakes the Allowed method, and establishes a known value for the system clock. Here is the revised test code:
[TestCase(1)]
[TestCase(5)]
[TestCase(12)]
public void Allowed_WhenCurrentDateIsInsideModificationWindow_ExpectTrue(
int startMonth)
{
// Arrange
var settings = new Mock<IModificationWindowSettings>();
settings
.SetupGet(e => e.StartMonth)
.Returns(startMonth);
settings
.SetupGet(e => e.StartDay)
.Returns(1);
var currentTime = new DateTime(
DateTime.Now.Year,
startMonth,
13);
var clock = new Mock<IClock>();
clock
.SetupGet(e => e.Now)
.Returns(currentTime); // Setup getter to return the test's clock
var classUnderTest =
new ModificationWindow(
clock.Object,
settings.Object);
// Act
var result = classUnderTest.Allowed();
// Assert
Assert.AreEqual(true, result);
}
If you’re looking for more depth and detail, take a look at this very good post on the IClock interface by Al Gonzalez: http://algonzalez.tumblr.com/post/679028234/iclock-a-test-friendly-alternative-to-datetime
The Gallio/MbUnit testing framework has its own IClock interface. I don’t like production deployments containing testing framework assemblies; however, the Gallio approach offers a few ideas to enhance the IClock interface.
Pros:
- Works well with an IoC Container/Dependency Injection approach
- Can work with .NET Framework 2.0 and later
- No impact on class-users and method-callers
- A system-wide approach
- Testability is greatly improved
Cons:
- System-wide change, some risk
- Can be disruptive when applied to legacy or Brownfield applications
I often use this approach when working in Greenfield application development or when major refactoring is warranted.
In the next part of this Fake Time series we’ll look at a mock isolation framework approach.
Like this:
Like Loading...
Related
Pingback: Four Ways to Fake Time, Part 2 « Ruthlessly Helpful
Pingback: Four Ways to Fake Time, Part 4 « Ruthlessly Helpful