2016-11-18 6 views
7

Я пишу простой тестовый пример, который проверяет, что мой контроллер вызывает кеш перед вызовом моего сервиса. Я использую xUnit и Moq для выполнения этой задачи.Правильный способ тестирования ASP.NET Core IMemoryCache

У меня возникла проблема, потому что 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)); 
} 

ли вы рекомендуете тестирование по-другому?

+2

Вы не должны испытывать 'IMemoryCache', так как он является частью библиотеки. Авторы библиотеки должны провести тестирование. –

ответ

14

Чтобы быть честным, я бы рекомендовал не тестировать это взаимодействие вообще.

Я бы подошел к этому тестовому примеру немного по-другому: вы действительно заботитесь о том, что после того, как ваш контроллер извлечет данные из вашего ISearchService, он не должен снова запрашивать данные и должен вернуть результат предыдущего вызова.

Тот факт, что IMemoryCache используется за кулисами, является лишь деталью реализации. Я даже не стал бы устанавливать тестовый двойной для него, я бы просто использовал экземпляр объекта Microsoft.Extensions.Caching.Memory.MemoryCache.

Мой новый тест будет выглядеть примерно так:

[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

Мне нравится ваш подход намного больше. Благодаря! –