RoboKozo
RoboKozo

Reputation: 5062

How to mock IAppCache's GetOrAddAsync method with It.IsAny AddItemFactory function

The method I'm testing has this bit of code in it:

var devices = await _cache.GetOrAddAsync(_cacheKey, AddItemFactory, DateTimeOffset.Now.AddMinutes(Configuration.DeviceInMinutes));`

Where _cache is an IAppCache and is being mocked with a new Mock<IAppCache>(); in the test

AddItemFactory has the following signature:

private async Task<IDictionary<int, Device>> AddItemFactory()

In my test I have written:

        _appCache.Setup(x => x.GetOrAddAsync(
            It.IsAny<string>(),
            It.IsAny<Func<Task<IDictionary<int, Device>>>>(),
            It.IsAny<DateTimeOffset>())
        ).ReturnsAsync(fakeDictionary);

When that .Setup gets evaluated, my test crashes with the following error:

System.NotSupportedException : Invalid setup on an extension method: x => x.GetOrAddAsync<IDictionary<int, Device>>(It.IsAny<string>(), It.IsAny<Func<Task<IDictionary<int, Device>>>>(), It.IsAny<DateTimeOffset>())

I think it's mocked correctly because Visual Studio intellisense isn't complaining and signatures being wrong but I don't understand what is happening.

Upvotes: 1

Views: 1701

Answers (1)

rgvlee
rgvlee

Reputation: 3193

That GetOrAddAsync method overload is an extension method; extension methods cannot be mocked.

GetOrAddAsync<T>(
      this IAppCache cache,
      string key,
      Func<Task<T>> addItemFactory,
      DateTimeOffset expires)

You need to mock the following method on the IAppCache interface:

Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> addItemFactory);

If you want to use Moq to do this, the library LazyCache.Testing.Moq (disclaimer - I am the author) implements this via the following setup:

cachingServiceMock.Setup(m => m.GetOrAddAsync(
    It.IsAny<string>(),
    It.IsAny<Func<ICacheEntry, Task<T>>>()))    
    .Returns(Task.FromResult(cacheEntryValue));

where cacheEntryValue is the task result value an invocation should return.

If you don't need to use Moq, given you are using a GetOrAdd* method, you should be able to use the built-in fake (MockCachingService) provided by the LazyCache library as per the doco.

Upvotes: 2

Related Questions