Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it an good idea to make a wrapper specifically for a DateTime that respresents Now?

I have been noticing lately that is really nice to use a DateTime representing 'now' as an input parameter for your methods, for mocking and testing purposes. Instead of every method calling DateTime.UtcNow themselves, I do it once in the upper methods and forward it on the lower ones.

So a lot of methods that need a 'now', have an input parameter DateTime now.

(I'm using MVC, and try to detect a parameter called now and modelbind DateTime.UtcNow to it)

So instead of:

public bool IsStarted
{
    get { return StartTime >= DateTime.UtcNow; }
}

I usually have:

public bool IsStarted(DateTime now)
{
    return StartTime >= now;
}

So my convention is at the moment, if a method has a DateTime parameter called now, you have to feed it with the current time. Of course this comes down to convention, and someone else can easily just throw some other DateTime in there as a parameter.

To make it more solid and static-typed I am thinking about wrapping DateTime in a new object, i.e. DateTimeNow. So in one of the most upper layers I will convert the DateTime to a DateTimeNow and we will get compile errors when, someone tries to fiddle in a normal DateTime.

Of course you can still workaround this, but at least if feels more that you are doing something wrong at point. Did anyone else ever went into this path? Are there any good or bad results on the long term that I am not thinking about?

like image 222
Dirk Boer Avatar asked Jun 01 '14 15:06

Dirk Boer


4 Answers

You can create an interface with necessary properties. Namely IClock and inject it as a dependancy.

interface IClock
{
    DateTime Now { get; }
    DateTime UtcNow { get; }
}

class SystemClock : IClock
{
    public DateTime Now { get { return DateTime.Now; } }
    public DateTime UtcNow { get { return DateTime.UtcNow ; } }
}

class TestDoubleClock : IClock
{
    public DateTime Now { get { return whateverTime; } }
    public DateTime UtcNow { get { return whateverTime ; } }
}

This way you can easily unit test your code which depends on DateTime. Passing the DateTime.Now everywhere as parameter sounds crappy. What if you need Now and also UtcNow and something else? Will you add three parameter for this purpose alone?

I suggest this interface technique to avoid ugly code with too many parameters which doesn't serves you much.

like image 92
Sriram Sakthivel Avatar answered Oct 31 '22 09:10

Sriram Sakthivel


I suggest creating an interface for providing a Now value:

public interface IDateTimeProvider
{
   DateTime Now { get; }
}

Then if you want to use current date in your MVC application just implement a class like this:

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

You can then inject that to your controller, mock it in unit tests and even replace with other implementation (for example if you decide to use UtcNow instead of Now in your code

like image 36
dotnetom Avatar answered Oct 31 '22 09:10

dotnetom


Yes it can be a good idea to wrap different timer implementation in a custom type, as you mention it.

I see three drawbacks in passing the time object around:

  • Accuracy:

Remember that the time accuracy in a given function will depend how how long it took to execute the code since the first call to DateTime.Now:

var date = DateTime.Now
func_1(date) // took 1 second to execute
func_2(date)  // took 1 second to execute
funf_3(date)  // date is now late by 3 seconds.
  • Performance:

You add a parameter to each of your function, and you add an indirection when getting the time (through your wrapper). In my case this overhead was noticeable in some scenarios.

  • Safety:

Except if you allow only one concrete type to encapsulate the time, you wont be able to prevent client code to write their own Time wrapper, still giving whatever time they want to your functions.

like image 24
quantdev Avatar answered Oct 31 '22 07:10

quantdev


I usually tackle this issue by letting the class accept a nowProvider as part of the contstructor like this:

public class ClassToTest
{
    private readonly Func<DateTime> _nowProvider;

    public ClassToTest(Func<DateTime> nowProvider)
    {
        _nowProvider = nowProvider;
    }

    public ClassToTest()
    {
        _nowProvider = () => DateTime.Now;
    }

    //snip
}

So in my tests I can do this:

var knownDate = new DateTime(2000, 1, 1);

var testObject = new ClassToTest(() => knownDate);
like image 37
DavidG Avatar answered Oct 31 '22 07:10

DavidG