围绕 NUnit 和 Moq 中的 DateTime 窗口进行单元测试



我正在跟随TDD Kata'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 和最小起订量来实现这一点?到目前为止,我看到的最佳解决方案是在构造函数中DI时间提供程序,但我宁愿不必在每个实例中提供通用时间提供程序来启用测试。

任何指导将不胜感激。

只需在调用 greet 方法时传入 DateTime 对象即可。 例

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

它将消除您对 DateTime.Now 的依赖。然后,您可以轻松地对其进行单元测试。

你应该从代码中获取对 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();
}
}

为了测试,所以不使用最小起订量,我们实现了特定的实现。

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 对象的实现。

另请参阅: 用于单元测试的日期时间接口

最新更新