C# 测试ASP.NET核心IMemoryCache的正确方法

C# 测试ASP.NET核心IMemoryCache的正确方法,c#,asp.net-core,moq,extension-methods,.net-core,C#,Asp.net Core,Moq,Extension Methods,.net Core,我正在编写一个简单的测试用例,测试我的控制器在调用我的服务之前调用缓存。我正在使用xUnit和Moq来完成任务 我面临一个问题,因为GetOrCreateAsync是一种扩展方法,框架无法模拟这些方法。我依靠内部细节来确定我可以模拟TryGetValue,而不必进行测试(请参阅) [理论,自动数据MOQ] 公共异步任务GivenPopulatedCacheDoesntCallService( 模拟缓存, SearchRequestViewModel输入, MyViewModel(预期) { 对象

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

我面临一个问题,因为
GetOrCreateAsync
是一种扩展方法,框架无法模拟这些方法。我依靠内部细节来确定我可以模拟
TryGetValue
,而不必进行测试(请参阅)

[理论,自动数据MOQ]
公共异步任务GivenPopulatedCacheDoesntCallService(
模拟缓存,
SearchRequestViewModel输入,
MyViewModel(预期)
{
对象expectedOut=预期;
隐藏物
.Setup(s=>s.TryGetValue(input.Serialized(),out expectedOut))
.返回(真);
var sut=newmycontroller(cache.Object,Mock.Of());
var实际值=等待sut.Search(输入);
断言。相同(预期、实际);
}
我无法入睡的事实是,我正在窥探MemoryCache实现的细节,它可以在任何时候改变

作为参考,这是SUT代码:

public async Task<MyViewModel> Search(SearchRequestViewModel request)
{
    return await cache.GetOrCreateAsync(request.Serialized(), (e) => search.FindAsync(request));
}
公共异步任务搜索(SearchRequestViewModel请求)
{
返回wait cache.GetOrCreateAsync(request.Serialized(),(e)=>search.FindAsync(request));
}

您是否建议以不同的方式进行测试?

老实说,我建议根本不要测试这种交互

我将以不同的方式处理这个测试用例:您真正关心的是,一旦控制器从
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);
}
[理论]
公共异步任务GivenResultAlreadyRetrieved\u不应再次调用服务()
{
//安排
var expected=新的MyViewModel();
var cache=newmemorycache(newmemorycacheoptions());
var searchService=new Mock();
var输入=新的SearchRequestViewModel();
搜索服务
.SetupSequence(s=>s.FindAsync(It.IsAny()))
.Returns(任务.FromResult(预期))
.Returns(Task.FromResult(新的MyViewModel());
var sut=新的MyController(缓存,searchService.Object);
//表演
var resultFromFirstCall=wait sut.Search(输入);
var resultFromSecondCall=wait sut.Search(输入);
//断言
相同(预期,resultFromFirstCall);
Assert.Same(应为resultFromSecondCall);
}

您不应该测试
IMemoryCache
,因为它是库的一部分。库的作者应该做测试。我更喜欢你的方法。谢谢
[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);
}