web-dev-qa-db-ja.com

ASP.NET Core IMemoryCacheをテストする適切な方法

サービスを呼び出す前にコントローラーがキャッシュを呼び出すことをテストする簡単なテストケースを書いています。タスクには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));
}

別の方法でテストすることをお勧めしますか?

14
Martín Coll

正直に言うと、この相互作用をまったくテストしないことをお勧めします。

このテストケースには少し異なるアプローチをとります。本当に重要なのは、コントローラーがISearchServiceからデータを取得したら、再度データを要求せず、前の呼び出しの結果を返すことです。

IMemoryCacheが舞台裏で使用されているという事実は、実装の詳細にすぎません。私はそれのためにテストdoubleを設定することさえ気にしないで、ただ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);
}
31
Botond Botos