【问题标题】:Correct way of unit-testing classes that use DateTimeOffset objects?使用 DateTimeOffset 对象的单元测试类的正确方法?
【发布时间】:2016-03-30 18:08:31
【问题描述】:

我将不胜感激有关如何正确测试使用 DateTimeOffset 实例的代码的信息或示例。我知道测试必须是确定性的。

那么,如何将应用程序与 DateTimeOffset 类隔离开来?当然,我希望能够使用假的 DateTimeOffset.Now 等。

在我的测试中,我应该使用类似的东西:

var myDate = new DateTimeOffset(2016, 3, 29, 12, 20, 35, 93, TimeSpan.FromHours(-3));

或者我会改用像 MyCustomDateTimeOffset 这样的包装类吗? 我应该在我的代码中根本不使用 DateTimeOffset 而是使用包装器吗?

【问题讨论】:

    标签: c# unit-testing datetime testing nunit


    【解决方案1】:

    正如fundamentals theorem 所说:

    我们可以通过引入额外的间接级别来解决任何问题。

    你真的不需要包装器,你只需要避免DateTimeOffset.Now/DateTimeOffset.UtcNow

    您可以通过以下几种方式来处理:

    • 如果您使用依赖注入,请编写一个IClock 接口,该接口公开Now/UtcNow 属性。

      public interface IClock
      {
          DateTimeOffset Now { get; }
          DateTimeOffset UtcNow { get; }
      }
      
      internal class Clock : IClock
      {
          public DateTimeOffset Now => DateTimeOffset.Now;
          public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
      }
      

      在您的测试中,您可以随意模拟界面。

    • 如果您希望继续使用静态属性,请编写一个静态类型,比如Clock,然后使用它。

      public static class Clock
      {
          internal static Func<DateTimeOffset> DateTimeOffsetProvider { get; set; }
              = () => DateTimeOffset.Now;
      
          public static DateTimeOffset Now => DateTimeOffsetProvider();
          public static DateTimeOffset UtcNow => DateTimeOffsetProvider().ToUniversalTime();
      }
      

      在您的测试中,您可以替换为DateTimeOffsetProvider

      这是一个 .NET 2 版本:

      public static class Clock
      {
          internal delegate DateTimeOffset DateTimeOffsetProviderDelegate();
          internal static DateTimeOffsetProviderDelegate DateTimeOffsetProvider { get; set; }
      
          public static DateTimeOffset Now { get { return DateTimeOffsetProvider(); } }
          public static DateTimeOffset UtcNow { get { return DateTimeOffsetProvider().ToUniversalTime(); } }
      
          static Clock()
          {
              DateTimeOffsetProvider = delegate() { return DateTimeOffset.Now; };
          }
      }
      

    【讨论】:

    • 谢谢,但第二个版本(我更喜欢它)是否适用于 .NET 2.0 项目?您知道.Net 2.0 是否还有其他方法可以做到这一点?谢谢
    • 为了在 .NET 2 中使用,您只需将 Func&lt;T&gt; 替换为自定义委托,我在答案中添加了一些代码。
    【解决方案2】:

    由于您不知道 DateTimeOffSet.Now 的值,因此您不能断言 DateTimeOffSet.Now 等于一个值。

    您可能应该重构以使用以下两种方法之一:

    • 依赖注入
    • 接口和包装器

    依赖注入 (DI)

    DI 的意思是你传递它而不是让方法确定日期。

    这种方法。 . .

    public void DoSomething()
    {
       var now = DateTimeOffSet.Now;
       // Do other stuff with the date
    }
    

    。 . .会改成这个方法

    public void DoSomething(DateTimeOffSet dtos)
    {
       // Do other stuff with the date
    }
    

    接口和封装

    您的另一个选择(尽管最后您也会使用 DI)是创建一个接口和一个包装器。然后在您的对象中使用接口而不是具体的 DateTimeOffSet,这样您就可以使用 MOQ 或其他测试库来最小化接口。以 SystemWrapper (https://github.com/jozefizso/SystemWrapper) 项目为例。

    【讨论】:

      猜你喜欢
      • 2012-01-05
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      • 2022-01-14
      • 2015-07-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      相关资源
      最近更新 更多