I have some unit tests that expects the 'current time' to be different than DateTime.Now and I don't want to change the computer's time, obviously. What's the best strategy to achieve this?
Thanks
I have some unit tests that expects the 'current time' to be different than DateTime.Now and I don't want to change the computer's time, obviously. What's the best strategy to achieve this?
Thanks
Mock Objects.
A mock DateTime that returns a Now that's appropriate for your test.
The best strategy is to wrap the current time in an abstraction and inject that abstraction into the consumer.
Alternatively, you can also define a time abstraction as an Ambient Context:
public abstract class TimeProvider
{
private static TimeProvider current =
DefaultTimeProvider.Instance;
public static TimeProvider Current
{
get { return TimeProvider.current; }
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
TimeProvider.current = value;
}
}
public abstract DateTime UtcNow { get; }
public static void ResetToDefault()
{
TimeProvider.current = DefaultTimeProvider.Instance;
}
}
This will enable you to consume it like this:
var now = TimeProvider.Current.UtcNow;
In a unit test, you can replate TimeProvider.Current
with a Test Double/Mock object. Example using Moq:
var timeMock = new Mock<TimeProvider>();
timeMock.SetupGet(tp => tp.UtcNow).Returns(new DateTime(2010, 3, 11));
TimeProvider.Current = timeMock.Object;
However, when unit testing with static state, always remember to tear down your fixture by calling TimeProvider.ResetToDefault()
.
You have some options for doing it:
Use mocking framework and use a DateTimeService (Implement a small wrapper class and inject it to production code). The wrapper implementation will access DateTime and in the tests you'll be able to mock the wrapper class.
Use Typemock Isolator, it can fake DateTime.Now and won't require you to change the code under test.
Use Moles, it can also fake DateTime.Now and won't require chance in production code.
Some examples:
Wrapper class using Moq:
[Test]
public void TestOfDateTime()
{
var mock = new Mock<IDateTime>();
mock.Setup(fake => fake.Now)
.Returns(new DateTime(2000, 1, 1));
var result = new UnderTest(mock.Object).CalculateSomethingBasedOnDate();
}
public class DateTimeWrapper : IDateTime
{
public DateTime Now { get { return DateTime.Now; } }
}
Faking DateTime directly using Isolator:
[Test]
public void TestOfDateTime()
{
Isolate.WhenCalled(() => DateTime.Now).WillReturn(new DateTime(2000, 1, 1));
var result = new UnderTest().CalculateSomethingBasedOnDate();
}
Disclaimer - I work at Typemock
Moles:
[Test]
public void TestOfDateTime()
{
MDateTime.NowGet = () => new DateTime(2000,1,1);
var result = new UnderTest().CalculateSomethingBasedOnDate();
}
Disclaimer - I work on Moles