测试ASP.NET Core IMemoryCache

时间:2016-11-18 19:25:50

标签: c# asp.net-core moq extension-methods .net-core

我正在编写一个简单的测试用例,测试我的控制器在调用我的服务之前调用缓存。我正在使用xUnit和Moq来完成任务。

我遇到了一个问题,因为GetOrCreateAsync<T>是一种扩展方法,而且这些方法无法模仿。我依靠内部细节来弄清楚我可以嘲笑TryGetValue并躲开我的测试(见https://github.com/aspnet/Caching/blob/c432e5827e4505c05ac7ad8ef1e3bc6bf784520b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L116

[Theory, AutoDataMoq]
public async Task GivenPopulatedCacheDoesntCallService(
    Mock<IMemoryCache> cache,
    SearchRequestViewModel input,
    MyViewModel expected)
{
    object expectedOut = expected;
    cache
        .Setup(s => s.TryGetValue(input.Serialized(), out expectedOut))
        .Returns(true);
    var sut = new MyController(cache.Object, Mock.Of<ISearchService>());
    var actual = await sut.Search(input);
    Assert.Same(expected, actual);
}

我无法入睡,因为我正在窥视MemoryCache实现细节,它可以随时改变。

作为参考,这是SUT代码:

public async Task<MyViewModel> Search(SearchRequestViewModel request)
{
    return await cache.GetOrCreateAsync(request.Serialized(), (e) => search.FindAsync(request));
}

您会建议采用不同的方式进行测试吗?

1 个答案:

答案 0 :(得分:25)

老实说,我建议不要测试这种互动。

我会稍微改变一下这个测试用例:您真正关心的是,一旦您的控制器从您的ISearchService检索数据,它就不应该再次请求数据,并且应该返回之前的结果呼叫。

在幕后使用IMemoryCache的事实只是一个实现细节。我甚至不打算为它设置测试双,我只会使用Microsoft.Extensions.Caching.Memory.MemoryCache对象的实例。

我的新测试看起来像这样:

[Theory]
public async Task GivenResultAlreadyRetrieved_ShouldNotCallServiceAgain()
{
    // Arrange
    var expected = new MyViewModel();
    object actualOut;

    var cache = new MemoryCache(new MemoryCacheOptions());
    var searchService = new Mock<ISearchService>();

    var input = new SearchRequestViewModel();

    searchService
        .SetupSequence(s => s.FindAsync(It.IsAny<SearchRequestViewModel>()))
        .Returns(Task.FromResult(expected))
        .Returns(Task.FromResult(new MyViewModel()));

    var sut = new MyController(cache, searchService.Object);

    // Act
    var resultFromFirstCall = await sut.Search(input);
    var resultFromSecondCall = await sut.Search(input);

    // Assert
    Assert.Same(expected, resultFromFirstCall);
    Assert.Same(expected, resultFromSecondCall);
}