我有一些单元测试,期望“当前时间”与DateTime不同。显然,我不想改变电脑的时间。
实现这一目标的最佳策略是什么?
我有一些单元测试,期望“当前时间”与DateTime不同。显然,我不想改变电脑的时间。
实现这一目标的最佳策略是什么?
当前回答
关于模拟DateTime有一个特别的注意事项。现在使用TypeMock…
DateTime的值。现在必须放置到一个变量中,以便正确地模拟。例如:
这行不通:
if ((DateTime.Now - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))
然而,这样做:
var currentDateTime = DateTime.Now;
if ((currentDateTime - message.TimeOpened.Value) > new TimeSpan(1, 0, 0))
其他回答
为系统添加一个假程序集(右键单击System reference=>添加假程序集)。
并在测试方法中写入:
using (ShimsContext.Create())
{
System.Fakes.ShimDateTime.NowGet = () => new DateTime(2014, 3, 10);
MethodThatUsesDateTimeNow();
}
我也遇到过同样的问题,但我发现微软的一个研究项目解决了这个问题。
http://research.microsoft.com/en-us/projects/moles/
mole是一个基于委托的。net测试存根和弯路的轻量级框架。mole可以用来绕过任何.NET方法,包括密封类型中的非虚拟/静态方法
// Let's detour DateTime.Now
MDateTime.NowGet = () => new DateTime(2000,1, 1);
if (DateTime.Now == new DateTime(2000, 1, 1);
{
throw new Exception("Wahoo we did it!");
}
示例代码是在原始代码的基础上修改的。
我已经按照其他人的建议做了,并将DateTime抽象为一个提供程序。这感觉不对,我觉得光是测试就做不下去了。今晚我将把这个应用到我的个人项目中。
测试依赖于系统的代码。DateTime,系统.dll必须被模拟。
我知道有两个框架可以做到这一点。微软的假货和罩衫。
微软的假货需要visual studio 2012的最后通牒,直接从康普顿出来。
Smocks是一个开放源代码,非常容易使用。可以使用NuGet下载。
下面是System的一个模拟。DateTime:
Smock.Run(context =>
{
context.Setup(() => DateTime.Now).Returns(new DateTime(2000, 1, 1));
// Outputs "2000"
Console.WriteLine(DateTime.Now.Year);
});
我们使用的是静态SystemTime对象,但是在运行并行单元测试时遇到了问题。我尝试使用Henk van Boeijen的解决方案,但在派生异步线程上有问题,最终以类似于下面的方式使用AsyncLocal:
public static class Clock
{
private static Func<DateTime> _utcNow = () => DateTime.UtcNow;
static AsyncLocal<Func<DateTime>> _override = new AsyncLocal<Func<DateTime>>();
public static DateTime UtcNow => (_override.Value ?? _utcNow)();
public static void Set(Func<DateTime> func)
{
_override.Value = func;
}
public static void Reset()
{
_override.Value = null;
}
}
来源:https://gist.github.com/CraftyFella/42f459f7687b0b8b268fc311e6b4af08
我很惊讶没有人提出一个最明显的方法:
public class TimeDependentClass
{
public void TimeDependentMethod(DateTime someTime)
{
if (GetCurrentTime() > someTime) DoSomething();
}
protected virtual DateTime GetCurrentTime()
{
return DateTime.Now; // or UtcNow
}
}
然后,您可以简单地在测试double中重写此方法。
在某些情况下,我还喜欢注入一个TimeProvider类,但对于其他情况,这就足够了。不过,如果需要在多个类中重用TimeProvider版本,我可能更喜欢它。
编辑:对于任何感兴趣的人来说,这被称为向类中添加“接缝”,在这个点上,您可以钩入它的行为来修改它(用于测试目的或其他),而无需实际更改类中的代码。