'UnitTest a method that is time-dependent

How would you test a method that is time-dependent?

A simple example could be:

public static int GetAge(DateTime birthdate)
{
    // ...
}

[TestMethod]
public void GetAgeTest()
{
    // This is only ok for year 2013 !
    Assert.AreEqual(GetAge(new DateTime(2000, 1, 1)), 13);
}

The problem is that it will work only for the current year (2013 in this case). As soon as a new year begins, I have to rewrite all time-dependent tests...

What is the best approach to Unit Test such a method?



Solution 1:[1]

Two main options:

  1. Create a service that isolates time-dependent functionality and inject its interface into classes that need it. Then you can mock this interface to get the behavior you need for testing.

  2. Use a detouring test framework like Moles (there are a few others out there, as well) that can reroute static method calls like DateTime.Now.

Solution 2:[2]

You could override the mechanism by which your GetAge method finds today's date.

If you wrap DateTime in another class you can then replace the actual calls with fake calls that return known values while unit testing.

You can use dependency injection to switch between the real implementation for production and the fake implementation for testing.

Solution 3:[3]

Also, don't forget the "Expected" argument is the first one in AreEquals.

[TestMethod]
public void GetAgeTest()
{
    int age = 13;
    Assert.AreEqual(age, GetAge(DateTime.Now.AddYears(age * -1));
}

Would also be better to split the test in Arrange - Act - Assert groups like this :

[TestMethod]
public void GetAgeTest()
{
    // Arrange
    int age = 13;

    // Act
    int result = GetAge(DateTime.Now.AddYears(age * -1);

    // Assert
    Assert.AreEqual(age, result);
}

Solution 4:[4]

Something like this would do the trick

var birthDate = new DateTime(DateTime.Now.Year - 13, 1, 1)
int age = GetAge(birthDate);
Assert.AreEqual(age, 13);

Solution 5:[5]

Another solution would be to use Microsoft Shimes and Fakes. Look at http://msdn.microsoft.com/en-us/library/hh549175.aspx

using (ShimsContext.Create())
{
    // Shim DateTime.Now to return a fixed date:
    System.Fakes.ShimDateTime.NowGet = () =>
    { 
        return new DateTime(fixedYear, 1, 1); 
    };
}

Solution 6:[6]

Maybe not the best option in this specific case, but you could also just extend the interface of your function to also required a DateTime now to be passed.

public static int GetAge(DateTime birthdate, DateTime now)
{
    // ...
}

[TestMethod]
public void GetAgeTest()
{
    Assert.AreEqual(GetAge(new DateTime(2000, 1, 1), new DateTime(2013, 1, 1)), 13);
}

You're basically delegating the responsibility of getting the current date to the caller (also pushing the unit test problem there though).

A nice side effect is that you really get a well defined "now" within the bounds of this function. On the ticks level, the moment of calling DateTime.Now would make a difference. You can describe the function on an abstract level as "given this birthdate and this current date, the age is".

Again, this might not be the best way to go in every situation, but I think it's worth considering in some.

Solution 7:[7]

Another solution is to use VirtualTime. , it simplifies unit testing time dependent apps, see how to use and examples here Unit Testing: DateTime.Now

Solution 8:[8]

You can consider time as a dependency and inject it into your method. I suggest using the IClock interface from the NodaTime. Then in your unit tests, you can use the FakeClock class from NodaTime.Testing.

Your modified method.

public static int GetAge(IClock clock, DateTime birthdate)
{
    now = clock.GetCurrentInstant().ToDateTimeUtc();
    // use now and birthdate to calculate age
}

Then you can write your unit test like this.

[TestMethod]
public void GetAgeTest()
{
    var clock = new FakeClock(Instant.FromUtc(2013, 1, 1, 0, 0, 0)); // Create a fake clock that always return 2013/01/01 0:00:00
    var birthDate = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    Assert.AreEqual(GetAge(clock, birthdate), 13); Always return 13
}

If you ever decide to move GetAge into its own class.

public class AClass
{
    private readonly IClock _clock;

    public AClass(IClock clock) => _clock = clock;

    public int GetAge(DateTime birthdate)
    {
        now = _clock.GetCurrentInstant().ToDateTimeUtc();
        // use now and birthdate to calculate age
    }
}

The FakeClock is a very powerful testing tool. You can read more about it from my blog post here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dan Bryant
Solution 2 ChrisF
Solution 3 Pierre-Luc Pineault
Solution 4 Claudio Redi
Solution 5 Bidou
Solution 6 Matthijs Wessels
Solution 7 Samuel
Solution 8 duongntbk