C# 单元测试与时间相关的方法
您将如何测试与时间相关的方法 一个简单的例子是:C# 单元测试与时间相关的方法,c#,.net,unit-testing,C#,.net,Unit Testing,您将如何测试与时间相关的方法 一个简单的例子是: public static int GetAge(DateTime birthdate) { // ... } [TestMethod] public void GetAgeTest() { // This is only ok for year 2013 ! Assert.AreEqual(GetAge(new DateTime(2000, 1, 1)), 13); } 问题在于,它只适用于本年度(本例中为2013年
public static int GetAge(DateTime birthdate)
{
// ...
}
[TestMethod]
public void GetAgeTest()
{
// This is only ok for year 2013 !
Assert.AreEqual(GetAge(new DateTime(2000, 1, 1)), 13);
}
问题在于,它只适用于本年度(本例中为2013年)。新的一年一开始,我就必须重写所有与时间相关的测试
使用这种方法进行单元测试的最佳方法是什么?两个主要选项:
DateTime.Now
您可以重写
GetAge
方法查找今天日期的机制
如果将DateTime
包装到另一个类中,则可以在单元测试时将实际调用替换为返回已知值的伪调用
您可以使用依赖项注入在用于生产的真实实现和用于测试的伪实现之间进行切换。类似的操作可以实现这一点
var birthDate = new DateTime(DateTime.Now.Year - 13, 1, 1)
int age = GetAge(birthDate);
Assert.AreEqual(age, 13);
另外,不要忘记“Expected”参数是arequals
中的第一个参数
[TestMethod]
public void GetAgeTest()
{
int age = 13;
Assert.AreEqual(age, GetAge(DateTime.Now.AddYears(age * -1));
}
最好将测试拆分为Arrange-Act-Assert组,如下所示:
[TestMethod]
public void GetAgeTest()
{
// Arrange
int age = 13;
// Act
int result = GetAge(DateTime.Now.AddYears(age * -1);
// Assert
Assert.AreEqual(age, result);
}
另一个解决方案是使用微软的Shimes和Fakes。 看
在这种特定情况下,可能不是最好的选择,但您也可以扩展函数的接口,使其也需要传递
DateTime now
public static int GetAge(DateTime birthdate, DateTime now)
{
// ...
}
[TestMethod]
public void GetAgeTest()
{
Assert.AreEqual(GetAge(new DateTime(2000, 1, 1), new DateTime(2013, 1, 1)), 13);
}
您基本上是将获取当前日期的责任委托给调用者(不过也将单元测试问题推到了那里)
一个很好的副作用是,在这个函数的范围内,您确实得到了一个定义良好的“now”。在滴答声级别上,调用DateTime.Now的时刻会有所不同。您可以抽象地将函数描述为“给定此生日和当前日期,年龄为”
再说一次,这可能不是在所有情况下的最佳方式,但我认为在某些情况下值得考虑。另一个解决方案是使用VirtualTime,它简化了与时间相关的应用程序的单元测试,请参见此处的使用方法和示例如果在新年前夕执行此测试,时间正好在新年转换时。@Dan Bryant:这是一个非常极端的情况。。。10万分之一。。。或1000.000.000.000中的1。。。我个人并不担心这件事,因为这几乎不可能发生,但这是个人的问题opinion@Dan:好样的!您可以通过喝一杯香槟酒而不是在关键时刻运行单元测试来避免这个问题;-)@克劳迪奥,这可能不像你想象的那么罕见;许多自动构建服务器被配置为深夜运行,并且通常不休假。这也是一个刻意简化的例子。较长时间内发生的更复杂操作可能会出现更复杂的故障情况,如闰年效应、夏令时效应、,等等。这些极端情况通常需要测试,如果你不能完全控制程序的时间测量,你就无法准确地再现它们。@DanBryant:我知道CI服务器会自动运行这些代码,即使在这种情况下,概率也是1/1000.000.000。我很感谢你的反馈,但我认为你对这件事考虑得太多了;罕见的竞争条件可能导致此测试失败。这是直接依赖于当前时间的测试方法的一个根本问题。@DanBryant即使在除夕夜自动测试套件有一次失败,我也不认为任何人会在工作中看到它,不管怎么说,hahaI尝试了,它成功了;然而,在我看来,这增加了太多的开销。此外,为了注入依赖项,我不能使用
静态
方法,这对于静态助手来说有点烦人…@Bidou。是的,对于一个相对简单的测试来说,这是一个开销,但对于更复杂的测试来说,这是一个开销。@Bidou,它确实增加了一些尴尬。然而,它提出了一个有趣的问题,这就是为什么测试对象首先依赖于时间信息?GetAge方法真的应该隐式地依赖DateTime吗?现在还是接受引用DateTime作为参数更有意义?这里的可测试性困难表明(不必要的?)依赖性可能会通过稍微不同的设计来消除。Moles似乎很有趣,我在这里找到了一个例子:不幸的是,Moq之类的模拟框架不支持路由或moqing之类的静态方法/属性DateTime。现在查看VitualTime,它简化了与时间相关的应用程序的单元测试,示例如下
public static int GetAge(DateTime birthdate, DateTime now)
{
// ...
}
[TestMethod]
public void GetAgeTest()
{
Assert.AreEqual(GetAge(new DateTime(2000, 1, 1), new DateTime(2013, 1, 1)), 13);
}