使用NUnit对DateTime控制器进行单元测试

3

我正在创建一个React / .NET Core项目,其中一个页面显示当前日期和时间。我的控制器类如下:

namespace TestingReactDotNet.Controllers {

    [Route ("api/[controller]")]
    public class DayTodayController : Controller {

        [HttpGet, Route ("GetDate")]
        public  string GetDate () {

            var info = $"Today is {DateTime.Now.ToString("dddd, dd MMMM yyyy")} and the time is {DateTime.Now.ToString("hh:mm tt")}";
            return info;

        }
    }
}

我正在尝试对此进行单元测试并模拟日期和时间,但我无法做到这一点(尽管我已经查看了其他 Stack Overflow 的问题并进行了谷歌搜索,但仍然存在困难)。我最初想使用 Moq ,但似乎不可能实现?

这是我目前为止的测试代码:

namespace TestingReactDotNetTests
{
    public class DayTodayTests
    {
        [Test]
        public void ReturnsDateAndTime()
        {
            var controller = new DayTodayController();
            string result = controller.GetDate();
            string expected = "Today is Tuesday 27 August 2019 and the time is 10:00";
            Assert.AreEqual(expected, result); 


        }
    }
}

我的问题是 - 我如何设置日期和时间,以便在字符串中调用 DateTime.Now 时,它不是当前的日期和时间?


有几种方法可以实现这个。例如:public class DayTodayController : Controller { internal DateTime? overrideNow; public DateTime Now { get { return overrideNow ?? DateTime.Now(); } } } 然后从你的单元测试中设置 overrideNow - AlwaysLearning
1
@AlwaysLearning,那是一个非常恶劣的黑客行为,你永远不应该这样做。 - DavidG
1个回答

5
通常实现这一点的方法是在所测试对象的构造函数中注入一个日期/时间提供程序。例如:
public interface IDateTimeProvider
{
    DateTime Now();
}

以及该接口的具体实现:

public class DateTimeProvider : IDateTimeProvider
{
    public DateTime Now() 
    {
        return Datetime.Now;
    }
}

你需要将 IDateTimeProvider 添加到 DI 容器中,类似于:
services.AddSingleton<IDateTimeProvider, DateTimeProvider>(); 

但这取决于你如何设置,使用的框架等等。

你的控制器将会改为类似下面的样子:

[Route ("api/[controller]")]
public class DayTodayController : Controller
{
    private readonly IDateTimeProvider _dateTimeProvider;

    public DayTodayController(IDateTimeProvider dateTimeProvider)
    {
        _dateTimeProvider = dateTimeProvider;
    }

    [HttpGet, Route ("GetDate")]
    public  string GetDate () {
        var dateTime = _dateTimeProvider.Now();
        var info = $"Today is {dateTime.ToString("dddd, dd MMMM yyyy")} and the time is {dateTime.ToString("hh:mm tt")}";
        return info;
    }
}

注意将DateTime分配给变量。多次调用DateTime.Now会给出不同的值,因此您需要操作一个实例来获得所需的行为。

现在,您可以使用非易碎的模拟接口进行测试:

public class DayTodayTests
{
    [Test]
    public void ReturnsDateAndTime()
    {
        //Arrange
        var mockedDateTimeProvider = new Mock<IDateTimeProvider>();
        mockedDateTimeProvider.Setup(dtp => dtp.Now()).Returns(new DateTime(2019, 8, 27, 10, 0, 0));
        var controller = new DayTodayController(mockedDateTimeProvider.Object);

        //Act
        string result = controller.GetDate();

        //Assert
        string expected = "Today is Tuesday 27 August 2019 and the time is 10:00 AM";

        Assert.AreEqual(expected, result); 
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接