Unit Testing: Datetime.Now

Unit Testing: DateTime.Now

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 replace 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().

Unit testing DateTime.Now

I'm not sure if passing some time provider to task constructor is a good idea.

It is a good idea. References to DateTime.Now are considered dependencies on global state and are discouraged from being used directly. About the constructor - it may be argued that doing this inside a constructor is a bad practice as well ("doing work inside constructors"), but that is a different topic.

The best practice for testability is to create a small wrapper interface to provide the current time which can be implemented against DateTime.Now or mocked for tests.

While some test frameworks like Microsoft Fakes are able to replace the DateTime.Now getter, this is complex to set up and maintain and should only be used when needing to test legacy code.

If you happen to already be using libraries like System.Reactive ("Rx.NET") or NodaTime, they already have abstractions over time ( IScheduler for rx, IClock in NodaTime).

What's a good way to overwrite DateTime.Now during testing?

My preference is to have classes that use time actually rely on an interface, such as

interface IClock
{
DateTime Now { get; }
}

With a concrete implementation

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

Then if you want, you can provide any other kind of clock you want for testing, such as

class StaticClock: IClock
{
DateTime Now { get { return new DateTime(2008, 09, 3, 9, 6, 13); } }
}

There may be some overhead in providing the clock to the class that relies on it, but that could be handled by any number of dependency injection solutions (using an Inversion of Control container, plain old constructor/setter injection, or even a Static Gateway Pattern).

Other mechanisms of delivering an object or method that provides desired times also work, but I think the key thing is to avoid resetting the system clock, as that's just going to introduce pain on other levels.

Also, using DateTime.Now and including it in your calculations doesn't just not feel right - it robs you of the ability to test particular times, for example if you discover a bug that only happens near a midnight boundary, or on Tuesdays. Using the current time won't allow you to test those scenarios. Or at least not whenever you want.

Unit testing code dependent on DateTime.Now

Kata is dependent on an implementation concretion when it should be dependent on an abstraction. That would allow Kata the flexibility at accept different implementations of the abstraction.

public class Kata {
private readonly IClock clock;

public Kata(IClock clock) {
this.clock = clock;
}

public int GetAgeFromDOB(DateTime birthday) {
var now = clock.Now;
var age = now.Year - birthday.Year;
if (birthday > now.AddYears(-age)) age--;
return age;
}
}

public interface IClock {
DateTime Now { get; }
}

The two implementations of IClock can be kept simple based on the requirements stated in the question.

The SystemClock class needs to implement a DateTime method that returns DateTime.Now

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

The StaticClock method needs to implement a DateTime method that returns a DateTime value that was passed to its constructor.

public class StaticClock : IClock {
public StaticClock(DateTime now) {
this.Now = now;
}

public DateTime Now { get; private set; }
}

Which should allow you to get the desired results.

Strategies for dealing with DateTime.Now in unit tests

Is it possible/advisable to try and mock the date?

Yes, not only that it is advisable but it is the only way to reliably unit test your code in isolation. Suppose that you wanted to test the following (meaningless) method:

public bool Foo()
{
return (DateTime.Now.DayOfWeek == DayOfWeek.Sunday);
}

It's more than obvious that you can't test it because it relies on a static method (the static Now property to be more precise). It's clear that components which are tightly coupled like this cannot be unit tested in isolation.

Now consider this improvement (separation of concerns with constructor DI):

private readonly Func<DateTime> _nowProvider;
public SomeClass(Func<DateTime> nowProvider)
{
_nowProvider = nowProvider;
}

public bool Foo()
{
return (_nowProvider().DayOfWeek == DayOfWeek.Sunday);
}

Now that's much better and easier to unit test. SomeClass no longer depends on a non-deterministic date. In the real application you would obviously instantiate SomeClass like this:

var s = new SomeClass(() => DateTime.Now);
s.Foo();

and in your unit test you will mock it so that you can verify both cases:

var subjectUnderTest = new SomeClass(() => new DateTime(2011, 1, 3));
var actual = subjectUnderTest.Foo();
// assertions, ...

Unit testing a function containing DateTime( now )

You could instead pass in the $date_start variable to the function, and when you call it in your test it would be the same every time ie

function testMymethod(){
$date_start = new DateTime('2011-01-01');
$class->getSomeInfo($id, $date_start);
//assertions
}

And your method signiture would change to:

    class myclass {
public function getSomeInfo($id, $date_start = new DateTime()){...}
}

How to test code that uses DateTime.now in Flutter?

If you use the clock package for code depending on DateTime.now() you can easily mock it.

Other than creating a custom wrapper around DateTime.now(), I don't think there is a better way than what the clock package provides.



Related Topics



Leave a reply



Submit