Unit Test – 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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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]

1
2
3
4
5
6
namespace TestDate;
 
public interface IServiceDate
{
    public DateTime GetDate();
}



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

[SERVICEDATE.CS]

1
2
3
4
5
6
7
8
9
namespace TestDate;
 
public class ServiceDate: IServiceDate
{
    public DateTime GetDate()
    {
        return DateTime.Now;   
    }
}



Finally, we modify the class Core:

[CORE.CS]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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 *