【问题标题】:Unit testing around DateTime windows in NUnit & Moq [duplicate]在 NUnit 和 Moq 中围绕 DateTime 窗口进行单元测试 [重复]
【发布时间】:2019-09-20 03:51:32
【问题描述】:

我正在关注 TDD Kata 'Greeter',其中我必须使用一个方法创建一个类,该方法根据一天中被调用的时间返回不同的输出。所以我有这个类 Greeter

public class Greeter : Greeter
{
    public DateTime time
    {
        get
        {
            return DateTime.Now;
        }
    }

    public Greeter() { }

    public string greet(string name)
    {
        string greeting = "Hello ";

        name = name.Substring(0, 1).ToUpper() + name.Substring(1);

        if(name.Length > 36)
        {
            name = name.Substring(0, 35);
        }

        if(time.Hour >= 6 && time.Hour <= 12)
        {
            greeting = "Good morning";
        } else if (time.Hour >= 18 && time.Hour <= 22)
        {
            greeting = "Good evening";
        } else if (time.Hour >=22 || time.Hour < 6)
        {
            greeting = "Good night";
        }

        return $"{greeting} {name}";
   }

}

Greeter 实现IGreeter

    public interface IGreeter
    {
        DateTime time
        {
            get;
        }
        string greet(string name);
    }

现在在单元测试方面,我必须在一天中的不同时间进行测试才能看到响应:上午Good morning ~,晚上Good evening ~,晚上Good night ~,和Hello ~在默认情况下。

所以我的问题是,我如何在考虑这些时间限制的情况下设计这些单元测试而不对类进行太多修改?据我所知,更改类只是为了启用单元测试被认为是代码异味。有没有办法使用 NUnit 和 Moq 来实现这一点?到目前为止我看到的最好的解决方案是在构造函数中 DI 时间提供程序,但我宁愿不必在每个实例化中提供通用时间提供程序只是为了启用测试。

任何指导将不胜感激。

【问题讨论】:

  • 这是一个骗局,但他们说答案不符合他们的要求
  • 为什么 SO UI 不显示指向骗子的链接?我对这个 kata 也有同样的问题
  • 只是为了记录,取决于时间称为时间耦合。例如,可以注入一个函数 Now() DateTime。现在我们可以在测试时提供一个具体的时间。参考:保持干燥,害羞,告诉其他人media.pragprog.com/articles/may_04_oo1.pdf

标签: c# .net nunit moq


【解决方案1】:

只需在调用 greet 方法时传入一个 DateTime 对象。 示例

greeter.greet(DateTime.Now, "steve");

它将消除您在时间方法中对 DateTime.Now 的依赖。然后您可以轻松地对其进行单元测试。

【讨论】:

  • 我也同意这种方法。重构以删除依赖项是使代码更可测试的好方法。赞成。
  • 不幸的是,要求声明不能更改函数签名
【解决方案2】:

您应该从代码中调用 DateTime 类。在此处使用接口并从外部提供实现。

public interface IDateTime
{
    DateTime Now();
}

public class Foo
{
    IDateTime dt;

    public DateTime GetActualTime
    {
        get
        {
            return dt.Now();
        }
    }

    public Foo(IDateTime dt)
    {
        this.dt = dt;
    }

    public string Bar()
    {
        return this.GetActualTime().ToString();
   }
}

为了测试,所以不使用 Moq,我们实现了一个特定的实现。

public class DateTimeTest : IDateTime
{
    DateTime dt;
    public DateTimeTest(DateTime testTime)
    {
        this.dt = testTime;
    }

    public DateTime Now()
    {
        return dt;
    }
}

并在测试中使用它

// Set your test time
DateTimeTest testTime = new DateTime(2019, 5, 1, 18, 04, 30);

// Create your sut
foo sut = new Foo(testTime);

// Call the function using the defined time
var result = sut.Bar();

在生产代码中,您需要一个使用 .NET DateTime 对象的实现。

另请参阅: DateTime Interface to be used for unit testing

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多