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: