使用Moq和Timer测试失败

时间:2016-07-21 17:47:29

标签: c# multithreading unit-testing moq mstest

我有以下测试用例:

private static readonlystring TEST_KEY = "SomeKey";
private static readonly object TEST_VALUE = 2;
private static readonlyTimeSpan TEST_EXPIRATION = TimeSpan.FromSeconds(2);

[TestMethod]
public void SetMethodStoresValueForCorrectTime()
{
    Mock<ObjectCache> mock = new Mock<ObjectCache>();

    // Setup mock's Set method
    mock.Setup(m => m.Set(TEST_KEY, TEST_VALUE, It.IsAny<DateTimeOffset>(), It.IsAny<string>()))
    .Callback(() => mock.Setup(m => m.Get(TEST_KEY, It.IsAny<string>())).Returns(TEST_VALUE));

    MyCache<object> instance = new MyCache<object>(mock.Object);

    // Add value to mocked cache
    instance.Set(TEST_KEY, TEST_VALUE, TEST_EXPIRATION);
    Assert.AreEqual(TEST_VALUE, instance.Get(TEST_KEY));

    // Configure a timer for item's expiration (Make mock's Get method return null)
    Timer timer = new Timer(_ => mock.Setup(m => m.Get(TEST_KEY, It.IsAny<string>())).Returns(null), null, TEST_EXPIRATION.Milliseconds, -1);

    // Wait for TimerCallback to trigger
    Thread.Sleep(TEST_EXPIRATION.Add(TimeSpan.FromSeconds(1)));
    Assert.IsNull(instance.Get(TEST_KEY)); // <-- Failing here

    timer.Dispose();
}

这里是MyCache<T>(相关部分):

public class MyCache<TSource> : ICache<TSource>
{
     private ObjectCache _innerCache;

     public MyCache(System.Runtime.Caching.ObjectCache innerCache)
     {
         _innerCache = innerCache;
     }

     // ...

     public TSource Get(string key)
     {
         if (key == null) throw new ArgumentNullException("key");
         object value = _innerCache.Get(key);
         return value != null ? (TSource)value : default(TSource);
     }

     public void Set(string key, TSource value, TimeSpan expiration)
     {
         if (key == null) throw new ArgumentNullException("key");
         _innerCache.Set(key, value, DateTimeOffset.UtcNow.Add(expiration));
     }
}

为什么测试失败? 它在最后一个断言中失败了:

  

Assert.IsNull 失败。

我在这里做错了吗?

1 个答案:

答案 0 :(得分:0)

我复制了你的代码,测试通过了我的机器。

但是你应该重新考虑你的测试,因为你正在尝试测试只是包装ObjectCache的MyCache。您不需要测试缓存过期(因为它是ObjectCache的一部分,并且应该是其单元测试的一部分),但只是MyCache正确地将Get和Set操作委托给ObjectCache。例如:

    [TestMethod]
    public void SetMethodStoresValueInInnerCache()
    {
        Mock<ObjectCache> mock = new Mock<ObjectCache>();

        MyCache<object> instance = new MyCache<object>(mock.Object);

        // Add value to mocked cache
        instance.Set(TEST_KEY, TEST_VALUE, TEST_EXPIRATION);

        mock.Verify(x => x.Set(TEST_KEY, TEST_VALUE, It.IsAny<DateTimeOffset>(), It.IsAny<string>()), Times.Once);
    }

你可以获得Get的等价物 如果您想测试MyCache是​​否正确设置了过期(代码DateTimeOffset.UtcNow.Add(expiration)),那么您可以创建一个类似ITime的接口并使用time.UtcNow(其中time是{的实例注入的{ {1}})在您的代码中 - 真正的实现将返回ITime并且在您的单元测试中您可以用固定时间模拟它(然后声明过期是固定时间加上TEST_EXPIRATION)