1

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.

RoboKozo
  • 4,981
  • 5
  • 51
  • 79
  • 1
    What's the signature of `IAppCache.GetOrAddAsync`? – mm8 Mar 23 '20 at 14:47
  • Does this answer your question? [Mocking Extension Methods with Moq](https://stackoverflow.com/questions/2295960/mocking-extension-methods-with-moq) – devNull Mar 23 '20 at 14:58
  • @mm8 it's what I have in the _appCache.Setup. (string, Func>, DateTimeOffset) – RoboKozo Mar 23 '20 at 15:55
  • @RoboKozo: You need to provide more details if you want any help. Your issue is not reproducible based on the information in your question. – mm8 Mar 23 '20 at 15:58
  • Hmm... well I can also inform that the app is using https://github.com/alastairtree/LazyCache – RoboKozo Mar 23 '20 at 16:11

1 Answers1

2

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.

rgvlee
  • 2,773
  • 1
  • 13
  • 20
  • I tested this out to see if it worked and it did! Thank you! FWIW I ended up just using a 'real' cache in my test: new LazyCache.CachingService(); – RoboKozo Mar 24 '20 at 15:10
  • I use GetAsync(key) from this package and the response time gets more than 20 ms. how can i fix this? @rgvlee – Nega Jun 10 '23 at 05:54
  • Which package, LazyCache.Testing.Moq? – rgvlee Jun 14 '23 at 00:19