我有一些单元测试,期望“当前时间”与DateTime不同。显然,我不想改变电脑的时间。

实现这一目标的最佳策略是什么?


当前回答

下面的代码为我工作:

  bizDeedMock.Verify(p => p.SetDeed(It.Is<DsPostList>(x => x.PostLists[0].registerDate.Year == DateTime.Now.Year)));
            bizDeedMock.Verify(p => p.SetDeed(It.Is<DsPostList>(x => x.PostLists[0].registerDate.Month == DateTime.Now.Month)));
            bizDeedMock.Verify(p => p.SetDeed(It.Is<DsPostList>(x => x.PostLists[0].registerDate.Day == DateTime.Now.Day)));

其他回答

在使用ITimeProvider时,我们被迫将它作为一个特殊的共享项目,必须从其他项目中引用它。但这使得依赖关系的控制变得复杂。

我们在。net框架中搜索ITimeProvider。我们搜索了NuGet包,发现了一个不能使用DateTimeOffset的包。

所以我们提出了自己的解决方案,它只依赖于标准库的类型。我们正在使用Func<DateTimeOffset>的实例。

如何使用

public class ThingThatNeedsTimeProvider
{
    private readonly Func<DateTimeOffset> now;
    private int nextId;

    public ThingThatNeedsTimeProvider(Func<DateTimeOffset> now)
    {
        this.now = now;
        this.nextId = 1;
    }

    public (int Id, DateTimeOffset CreatedAt) MakeIllustratingTuple()
    {
        return (nextId++, now());
    }
}

如何注册

自发法克

builder.RegisterInstance<Func<DateTimeOffset>>(() => DateTimeOffset.Now);

(未来的编辑请在这里附上你的案例)。

如何进行单元测试

public void MakeIllustratingTuple_WhenCalled_FillsCreatedAt()
{
    DateTimeOffset expected = CreateRandomDateTimeOffset();
    DateTimeOffset StubNow() => expected;
    var thing = new ThingThatNeedsTimeProvider(StubNow);

    var (_, actual) = thing.MakeIllustratingTuple();

    Assert.AreEqual(expected, actual);
}

模拟对象。

一个模拟DateTime,返回适合您的测试的Now。

关于模拟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))

测试依赖于系统的代码。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);
});

关于@crabcrusherclamcollector的回答,在EF查询中使用这种方法时存在问题。NotSupportedException: LINQ to Entities不支持LINQ表达式节点类型“Invoke”。我将实现修改为:

public static class SystemTime
    {
        private static Func<DateTime> UtcNowFunc = () => DateTime.UtcNow;

        public static void SetDateTime(DateTime dateTimeNow)
        {
            UtcNowFunc = () => dateTimeNow;
        }

        public static void ResetDateTime()
        {
            UtcNowFunc = () => DateTime.UtcNow;
        }

        public static DateTime UtcNow
        {
            get
            {
                DateTime now = UtcNowFunc.Invoke();
                return now;
            }
        }
    }