2016-11-18 5 views
7

Ich schreibe einen einfachen Testfall, der testet, dass mein Controller den Cache aufruft, bevor er meinen Dienst aufruft. Ich verwende xUnit und Moq für die Aufgabe.Korrekte Art, ASP.NET Core zu testen IMemoryCache

Ich habe ein Problem, weil GetOrCreateAsync<T> eine Erweiterungsmethode ist, und diese können nicht durch das Framework verspottet werden. Ich verließ sich auf interne Details, um herauszufinden, ich TryGetValue statt und weg mit meinem Test verspotten kann (siehe 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); 
} 

Ich kann nicht mit der Tatsache, schlafen, dass ich in die Memoryimplementierungsdetails bin spähen und es kann sich jederzeit ändern.

Als Referenz ist dies der SUT-Code:

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

Würden Sie anders Testen empfehlen?

+2

Sie sollten nicht testen 'IMemoryCache' da es Teil einer Bibliothek ist. Die Autoren der Bibliothek sollten die Tests durchführen. –

Antwort

14

Um ehrlich zu sein würde ich empfehlen, diese Interaktion überhaupt nicht zu testen.

Ich würde diesen Testfall ein wenig anders angehen: Was Sie wirklich interessiert ist, dass, sobald Ihr Controller Daten von Ihrem ISearchService abgerufen hat, sollte es die Daten nicht erneut anfordern und sollte das Ergebnis des vorherigen Anrufs zurückgeben.

Die Tatsache, dass ein IMemoryCache hinter den Kulissen verwendet wird, ist nur ein Implementierungsdetail. Ich würde nicht mal ein Testdoppel dafür aufbauen, ich würde einfach eine Instanz des Objekts Microsoft.Extensions.Caching.Memory.MemoryCache verwenden.

Mein neuer Test würde wie folgt aussehen:

[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); 
} 
+0

Ich mag Ihren Ansatz viel mehr. Vielen Dank! –

Verwandte Themen