使用 DateTimeOffset 对象的单元测试 类 的正确方法?
Correct way of unit-testing classes that use DateTimeOffset objects?
有关如何正确测试使用 DateTimeOffset 实例的代码的信息或示例,我将不胜感激。我知道测试必须是确定性的。
那么,如何将应用程序与 DateTimeOffset classes 隔离开来?当然,我希望能够使用假的 DateTimeOffset.Now,等等
在我的测试中,我应该使用类似的东西吗:
var myDate = new DateTimeOffset(2016, 3, 29, 12, 20, 35, 93, TimeSpan.FromHours(-3));
或者我会改用像 MyCustomDateTimeOffset 这样的包装器 class 吗?
我是否应该在我的代码中完全不使用 DateTimeOffset 而是使用包装器?
正如 fundamentals theorem 所说:
We can solve any problem by introducing an extra level of indirection.
你真的不需要包装器,你需要的只是避免 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; };
}
}
因为您不知道 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) 项目作为示例。
有关如何正确测试使用 DateTimeOffset 实例的代码的信息或示例,我将不胜感激。我知道测试必须是确定性的。
那么,如何将应用程序与 DateTimeOffset classes 隔离开来?当然,我希望能够使用假的 DateTimeOffset.Now,等等
在我的测试中,我应该使用类似的东西吗:
var myDate = new DateTimeOffset(2016, 3, 29, 12, 20, 35, 93, TimeSpan.FromHours(-3));
或者我会改用像 MyCustomDateTimeOffset 这样的包装器 class 吗? 我是否应该在我的代码中完全不使用 DateTimeOffset 而是使用包装器?
正如 fundamentals theorem 所说:
We can solve any problem by introducing an extra level of indirection.
你真的不需要包装器,你需要的只是避免 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; }; } }
因为您不知道 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) 项目作为示例。