C# – How to manage correctly a Date type variable

By | 05/10/2022

In this post, we will see how to manage correctly a Date type variable.
Many times in our code, when we have to define a variable with the current date and time, we use Datetime.Now… or at least, this is what I do…..
In theory this is correct but, if we have to set up something based on this value, it may be impossible to create Unit Tests.

We start creating a Console application called TestDate where we will add a class called Core, so defined:

[CORE.CS]

namespace TestDate;

public class Core
{
    public string Greeting()
    {
        DateTime today = DateTime.Now;

        if (today.Hour < 12)
            return "Good Morning";

        if (today.Hour < 19)
            return "Good Afternoon";

        return "Good Evening";
    }
}



Then, we add a xUnit project called UnitTest (I have a lot of imagination…) where we will create a class called UnitTestGreeting, so defined:

[UNITTESTGREETING.CS]

using Moq;
using System;
using TestDate;
using Xunit;

namespace UnitTest;

public class UnitTestGreeting
{
    const string GreetingMornig = "Good Morning";
    const string GreetingAfternoon = "Good Afternoon";
    const string GreetingEvening = "Good Evening";

    private readonly Core objCore;

    public UnitTestGreeting()
    {
        objCore = new Core();
    }

    [Fact]
    public void TestCore_WhenDateLessThan12_ShouldReturnGoodMorning()
    {
        // Arrange
        string result = string.Empty;

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingMornig, result);
    }

    [Fact]
    public void TestCore_WhenDateLessThan19_ShouldReturnGoodAfternoon()
    {
        // Arrange
        string result = string.Empty;

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingAfternoon, result);
    }

    [Fact]
    public void TestCore_WhenDateGreaterThan19_ShouldReturnGoodEvening()
    {
        // Arrange
        string result = string.Empty;

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingEvening, result);
    }
}



We can see that these tests depend of when we run them.
In fact, if I run now (it is 00:08 am) the tests, the only test that will pass is the first:

The problem is that we cannot modify the Date because, it is a value internal at the method Greeting().
How can we fix it?
It is very easy…
We create a new interface called IServiceDate where we will define a method called GetDate(), used to get the current datetime:

[ISERVICEDATE.CS]

namespace TestDate;

public interface IServiceDate
{
    public DateTime GetDate();
}



Then, we create a class called ServiceDate where we will implement the GetDate method:

[SERVICEDATE.CS]

namespace TestDate;

public class ServiceDate: IServiceDate
{
    public DateTime GetDate()
    {
        return DateTime.Now;    
    }
}



Finally, we modify the class Core:

[CORE.CS]

namespace TestDate;

public class Core
{
    private readonly IServiceDate _serviceDate;

    public Core(IServiceDate serviceDate)
    {
        _serviceDate = serviceDate;
    }

    public string Greeting()
    {
        DateTime today = _serviceDate.GetDate();

        if (today.Hour < 12)
            return "Good Morning";

        if (today.Hour < 19)
            return "Good Afternoon";

        return "Good Evening";
    }
}



In this way we have a more maintainable code and we can use Moq to set up the return value of GetDate(), in order to run the tests with correct values:

[UNITTESTGREETING.CS]

using Moq;
using System;
using TestDate;
using Xunit;

namespace UnitTest;

public class UnitTestGreeting
{
    const string GreetingMornig = "Good Morning";
    const string GreetingAfternoon = "Good Afternoon";
    const string GreetingEvening = "Good Evening";

    private readonly Core objCore;

    private readonly Mock<IServiceDate> objServiceDate;

    public UnitTestGreeting()
    {
        objServiceDate = new Mock<IServiceDate>();
        objCore = new Core(objServiceDate.Object);
    }

    [Fact]
    public void TestCore_WhenDateLessThan12_ShouldReturnGoodMorning()
    {
        // Arrange
        string result = string.Empty;
        DateTime today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 11, 00, 00);
        objServiceDate.Setup(setup => setup.GetDate()).Returns(today);

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingMornig, result);
    }

    [Fact]
    public void TestCore_WhenDateLessThan19_ShouldReturnGoodAfternoon()
    {
        // Arrange
        string result = string.Empty;
        DateTime today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 18, 00, 00);
        objServiceDate.Setup(setup => setup.GetDate()).Returns(today);

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingAfternoon, result);
    }

    [Fact]
    public void TestCore_WhenDateGreaterThan19_ShouldReturnGoodEvening()
    {
        // Arrange
        string result = string.Empty;
        DateTime today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 22, 00, 00);
        objServiceDate.Setup(setup => setup.GetDate()).Returns(today);

        // Act
        result = objCore.Greeting();

        // Assert
        Assert.Equal(GreetingEvening, result);
    }
}



Now, if we run the tests again, these will be the results:



Leave a Reply

Your email address will not be published. Required fields are marked *