Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test logic which is dependent on current date

I have this method which is dependent on current date. It checks if today is Sun, Mon, Tue or Wed, then it gives 5 days of lead time for arrival of shipped items. If its Thur, Fri or Sat then it gives 6 days of lead time to account for the weekend.

private DateTime GetEstimatedArrivalDate()
{
    DateTime estimatedDate; 
    if (DateTime.Now.DayOfWeek >= DayOfWeek.Thursday)
    {
        estimatedDate = DateTime.Now.Date.AddDays(6);
    }
    else
    {
        estimatedDate = DateTime.Now.Date.AddDays(5);
    }
    return estimatedDate; 
}

The actual estimation logic is more complex. I have simplified it for the purpose of this question. My question is how do I write a unit test for something like this which depends on todays date?

like image 560
Rohit Agarwal Avatar asked Feb 17 '10 20:02

Rohit Agarwal


2 Answers

You need to pass the current date in as a parameter:

private DateTime GetEstimatedArrivalDate(DateTime currentDate)
{
    DateTime estimatedDate; 
    if (currentDate.DayOfWeek >= DayOfWeek.Thursday)
    {
        estimatedDate = currentDate.AddDays(6);
    }
    else
    {
        estimatedDate = currentDate.AddDays(5);
    }
    return estimatedDate; 
}

In real code you call it like this:

DateTime estimatedDate = GetEstimatedArrivalDate(DateTime.Now.Date);

Then you can test it as follows:

DateTime actual = GetEstimatedArrivalDate(new DateTime(2010, 2, 10));
DateTime expected = ...;
// etc...

Note that this also fixes a potential bug in your program where the date changes between consecutive calls to DateTime.Now.

like image 57
Mark Byers Avatar answered Oct 12 '22 04:10

Mark Byers


Generally speaking, you'd want to abstract the method of obtaining the current date and time behind an interface, eg:

public interface IDateTimeProvider
{
    DateTime Now { get; }
}

The real service would be:

public class DateTimeProvider: IDateTimeProvider
{
    public DateTime Now
    {
        get
        {
            return DateTime.Now;
        }
    }
}

And a test service would be:

public class TestDateTimeProvider: IDateTimeProvider
{
    private DateTime timeToProvide;
    public TestDateTimeProvider(DateTime timeToProvide)
    {
        this.timeToProvide = timeToProvide;
    }

    public DateTime Now
    {
        get
        {
            return timeToProvide;
        }
    }
}

For services that require the current time, have them take an IDateTimeProvider as a dependency. For the real thing, pass a new DateTimeProvider(); when you're a component, pass in a new TestDateTimeProvider(timeToTestFor).

like image 25
Erik Forbes Avatar answered Oct 12 '22 05:10

Erik Forbes