[英]Proper way of testing ASP.NET Core IMemoryCache
I'm writing a simple test case that tests that my controller calls the cache before calling my service. 我正在编写一个简单的测试用例,测试我的控制器在调用我的服务之前调用缓存。 I'm using xUnit and Moq for the task.
我正在使用xUnit和Moq来完成任务。
I'm facing an issue because GetOrCreateAsync<T>
is an extension method, and those can't be mocked by the framework. 我遇到了一个问题,因为
GetOrCreateAsync<T>
是一个扩展方法,并且框架无法模拟这些方法。 I relied on internal details to figure out I can mock TryGetValue
instead and get away with my test (see https://github.com/aspnet/Caching/blob/c432e5827e4505c05ac7ad8ef1e3bc6bf784520b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L116 ) 我依靠内部细节来弄清楚我可以模拟
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);
}
I can't sleep with the fact that I'm peeking into the MemoryCache implementation details and it can change at any point. 我无法入睡,因为我正在窥视MemoryCache实现细节,它可以在任何时候改变。
For reference, this is the SUT code: 作为参考,这是SUT代码:
public async Task<MyViewModel> Search(SearchRequestViewModel request)
{
return await cache.GetOrCreateAsync(request.Serialized(), (e) => search.FindAsync(request));
}
Would you recommend testing any differently? 您会建议以不同方式进行测试吗
To be honest I would recommend not to test this interaction at all. 说实话,我建议不要测试这种互动。
I would approach this test case a bit differently: what you really care about is that once your controller retrieved data from your ISearchService
it shouldn't request the data again and should return the result from the previous call. 我会稍微改变一下这个测试用例:你真正关心的是,一旦你的控制器从你的
ISearchService
检索数据,它就不应该再次请求数据,而应该返回前一次调用的结果。
The fact that an IMemoryCache
is used behind the scenes is just an implementation detail. 在幕后使用
IMemoryCache
的事实只是一个实现细节。 I wouldn't even bother setting up a test double for it, I would just use an instance of the Microsoft.Extensions.Caching.Memory.MemoryCache
object. 我甚至不打算为它设置一个测试双,我只使用一个
Microsoft.Extensions.Caching.Memory.MemoryCache
对象的实例。
My new test would look something like this: 我的新测试看起来像这样:
[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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.