如何针对单元测试目的改变“今天的日期”?

时间:2010-03-20 04:36:01

标签: c# unit-testing datetime

我使用VS2008定位.NET 2.0 Framework,为了以防万一,我不能改变它:)

我有一个DateCalculator课程。其方法GetNextExpirationDate尝试在内部使用DateTime.Today作为基准日期来确定下一个到期日期。

在我编写单元测试时,我意识到我想针对不同的'今天'日期测试GetNextExpirationDate

最好的方法是什么?以下是我考虑过的一些替代方案:

  • 使用参数baselineDate公开属性/重载方法,并仅在单元测试中使用它。在实际的客户端代码中,忽略属性/重载方法,而不是默认baselineDateDateTime.Today的方法。我不愿意这样做,因为它使得DateCalculator类的公共接口变得笨拙。
  • 创建一个名为baselineDate的受保护字段,该字段在内部设置为DateTime.Today。测试时,从DateCalculatorForTesting派生DateCalculator并通过构造函数设置baslineDate。它保持公共接口干净,但仍然不是很好 - baselineDate受到保护,并且需要派生类,这两者都仅用于测试。
  • 使用扩展方法。我在添加ExtensionAttribute后尝试了这个,然后意识到它不起作用,因为扩展方法无法访问私有/受保护的变量。我最初认为这是一个非常优雅的解决方案。 :(

我有兴趣听听别人的想法。

4 个答案:

答案 0 :(得分:4)

您可以使用提供基准日期的界面。通常,您将使用返回DateTime.Today的实现,但出于测试目的,可以让您的单元测试提供日期。

如果需要在数据库服务器或其他不一定运行代码的计算机上使用当前日期,这也很有用。

事实上,这是一个相同的问题,有一些比我更详细的答案:Unit Testing: DateTime.Now

答案 1 :(得分:1)

一个选项是创建一个带有DateTime的内部重载,并将实际实现委托给它:

public DateTime GetNextExpirationDate()
{
  return GetNextExpirationDate(DateTime.Today);
}

internal DateTime GetNextExpirationDate(DateTime after)
{
  // implementation goes here
}

然后,您可以使用InternalsVisibleToAttribute使过载对测试程序集可见,然后您的测试程序集可以使用自己选择的DateTime值调用它。

答案 2 :(得分:1)

我通常会在抽象/界面内收集对操作系统的调用,以便我可以轻松地对其进行测试..就像上面提到的安德鲁一样。

但只考虑问题中提到的需要;我觉得'Subclass and Override'是完成这项工作的最简单,最不具侵略性的方法 - OP提到的选项2。

public class DateCalculator
{
  public DateTime GetNextExpirationDate() {  // call to GetBaseLineDate() to determine result }
  virtual protected GetBaseLineDate() {  return DateTime.Today; }
}

// in your test assembly
public class DateCalcWithSettableBaseLine : DateCalculator
{
  public DateTime BaseLine { get; set;}
  override protected GetBaseLineDate()
  {    return this.BaseLine;  }
}

答案 3 :(得分:0)

您可以在正在进行测试的计算机上更改操作系统的时钟。这很简单,也很透明​​,但请注意文件时间戳的问题。