ASP.NET Core IMemoryCache的正确测试方法

25

我正在编写一个简单的测试用例,测试我的控制器在调用我的服务之前是否调用缓存。为此,我使用了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));
}

你会推荐以不同的方式进行测试吗?


4
由于IMemoryCache是一个库的一部分,所以你不应该对它进行测试。 库的作者应该进行测试。 - Botond Botos
1个回答

57

说实话,我建议根本不要测试这个交互。

我会以稍微不同的方式来处理这个测试用例:您真正关心的是一旦控制器从 ISearchService 检索数据,它不应再次请求数据,并应返回上一次调用的结果。

后台使用了 IMemoryCache 只是一个实现细节。我甚至不会费心为其设置测试替身,而是直接使用 Microsoft.Extensions.Caching.Memory.MemoryCache 对象的一个实例。

我的新测试大致如下:

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

    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);
}

我非常喜欢你的方法。谢谢! - Martín Coll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接