这个问题在这里已有答案:
我跟随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时间提供程序,但是我不想在每个实例化中提供通用时间提供程序来启用测试。
任何指导将不胜感激。
只需在调用greet方法时传入DateTime对象。例
greeter.greet(DateTime.Now, "steve");
它将删除您在time方法中对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();
}
}
为了测试,所以不使用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对象的实现。