I’m sure alot of you have come across a method that uses DateTime.Now at some point in your lives. Normally this is fine and nobody blinks an eyelid… until we need to unit test it.
Consider the follow code:
public class MyEntity
{
public DateTime Created { get; set; }
}
public class MyRepository
{
public void UpdateMyEntity(MyEntity entity)
{
entity.Created = DateTime.Now;
}
}
Unless you are one of one of those fancy pants with TypeMock and the ability to fake DateTime.Now, the rest of use must look elsewhere for a solution. Here are three different ways to solve this.
1. Wrap your DateTime calls with another class
Some people prefer using a static “Clock” class to handle this which can be easily faked out during your unit testing.
public static class Clock
{
public static Func<DateTime> Now = () => DateTime.Now;
}
This approach, while decoupling your dependency on System.DateTime is a bit of overkill and requires all developers on the project to be aware of it and to use it.
2.Use an Interface and your favourite Isolation Framework
public interface IClock
{
DateTime Now {get;}
}
public class SystemClock : IClock
{
public DateTime Now { get { return DateTime.Now; } }
}
You can now use an isolation framework such as Rhino.Mocks to fake the call to Now();
3. Use a DateTime Comparer that accepts a range
While not 100% accurate to the millisecond, this approach is my prefered approach as you don’t have to change to your code just to unit test it. No littering your code with IClock dependencies or using a delegate to return the current DateTime.Now (although one could argue that DateTime.Now shouldn’t be a property to begin with). This approach asserts that the Created property that is set in MyRepository.Update is within a certain range.
/// <summary>
/// Helper class to compare 2 values are within a certain range.
/// </summary>
public class DateComparer : IComparer<DateTime>
{
public TimeSpan MarginOfError { get; private set; }
public DateComparer(TimeSpan marginOfError)
{
MarginOfError = marginOfError;
}
public int Compare(DateTime x, DateTime y) // x = expected, y = actual
{
var margin = x - y;
if (margin <= MarginOfError)
return 0;
return new Comparer(CultureInfo.CurrentUICulture).Compare(x, y);
}
}
You can now write the follow test:
public void MyRepository_UpdateTest()
{
var repository = new MyRepository();
var entity = new MyEntity();
repository.UpdateMyEntity(entity);
var comparer = new DateComparer(new TimeSpan(0, 0, 0, 5));
Assert.IsTrue(comparer.Compare(entity.Created, DateTime.Now) == 0);
}