Skip to main content

Helper class that will return DateTime.Now, but can be changed to deal with tests.

---
title: Dealing with Dotnet DateTime in Tests
subtitle: Helper class that will return DateTime.Now, but can be changed to deal with tests.
author: Ayende Rahien
date: July 7, 2008
source: https://ayende.com/blog/3408/dealing-with-time-in-tests
notoc: false
---

One of the more annoying things to test is time sensitive code.

I just spent five minutes trying to figure out why this code if failing:

```csharp
repository.ResetFailures(failedMsgs);
var msgs = repository.GetAllReadyMessages();
Assert.AreEqual(2, msgs.Length);
```

Reset failures will set the retry time of the failed messages to 2 seconds in
the features. GetAllReadyMessages will only get messages that are ready now.

Trying to test that can be a real pain. One solution that I have adopted across
all my projects is introducing a separate concept of time:

> `SystemDateTime.cs` example from [dotnetcore.samples](https://github.com/cmendible/dotnetcore.samples/blob/master/remove.datetime.dependency.test/SystemDateTime.cs) on Github.

```csharp
namespace MyServices
{
    using System;

    /// <summary>
    /// Helper class that will return DateTime.Now, but can be changed to deal with tests.
    /// https://ayende.com/blog/3408/dealing-with-time-in-tests
    /// </summary>
    public static class SystemDateTime
    {

        /// <summary>
        /// Gets a function that when evaluated returns a local date and time.
        /// </summary>
        /// <returns>A function that when evaluated returns a local date and time.</returns>
        public static Func<DateTime> Now = () => DateTime.Now;
    }
}
```

Now, instead of calling DateTime.Now, I make the call to `SystemDateTime.Now()`, and
get the same thing. This means that I can now test the code above easily, using:

```csharp
SystemDateTime.Now = () => new DateTime(2000,1,1);
repository.ResetFailures(failedMsgs);
SystemDateTime.Now = () => new DateTime(2000,1,2);
var msgs = repository.GetAllReadyMessages();
Assert.AreEqual(2, msgs.Length);
```

### Example Service

> `TimeDependentService.cs` example from [dotnetcore.samples](https://github.com/cmendible/dotnetcore.samples/blob/master/remove.datetime.dependency.test/TimeDependentService.cs) on Github.

```cs
namespace MyServices
{
    using System;

    /// <summary>
    /// A time dependent on DateTime.Now
    /// </summary>
    public class TimeDependentService
    {

        /// <summary>
        /// Tells if it's morning.
        /// </summary>
        /// <returns>true if it's before 12</returns>
        public bool OldIsMorning()
        {
            return DateTime.Now.Hour < 12;
        }

        /// <summary>
        /// Tells if it's morning using the helper by Ayende.
        /// </summary>
        /// <returns>true if it's before 12</returns>
        public bool IsMorning()
        {
            return SystemDateTime.Now().Hour < 12;
        }
    }
}
```

### Example Tests

> `Tests.cs` example from [dotnetcore.samples](https://github.com/cmendible/dotnetcore.samples/blob/master/remove.datetime.dependency.test/Tests.cs) on Github.

```cs
namespace Tests
{
    using System;
    using MyServices;
    using Xunit;

    public class Tests
    {
        /// <summary>
        /// If this test is run after noon it will fail cause of the dependency on the system time.
        /// </summary>
        [Fact]
        public void Will_Fail_After_Noon()
        {
            var svc = new TimeDependentService();
            Assert.True(svc.OldIsMorning());
        }

        /// <summary>
        /// This test will run OK no matter the systems time, cause we are using the Ayende
        /// approach to tests with time dependencies.
        /// </summary>
        [Fact]
        public void Will_Run_OK_No_Matter_The_Real_Time()
        {
            // Force the time to 11 AM
            SystemDateTime.Now = () => new DateTime(2016, 10, 10, 11, 0, 0);
            var svc = new TimeDependentService();
            Assert.True(svc.IsMorning());
        }
    }
}
```

This is a really painless way to deal with this issue.