6

I'm trying to use Moq to test integration between a WebAPI controller and a Redis database, using the StackExchange.Redis client, and cannot work out how to set up a verifiable expectation on a mocked async method that includes a callback or some other assertion behaviour.

Ordinarily, I'd use the following syntax:

const string KEY = "some_key";
var db = new Mock<IDatabase>();
db.Setup(d => d.HashSetAsync(KEY, It.IsAny<HashEntry[]>(),It.IsAny<CommandFlags>()))
    .Callback<RedisKey,HashEntry[],CommandFlags>((key, hash, flags) => {
                hash.ShouldContain(entry => entry.Name == "customerid");
                hash.ShouldContain(entry => entry.Name == "quotenumber");
     })
     .Verifiable();

But this is giving me:

'Moq.Language.Flow.IReturnsThrows<StackExchange.Redis.IDatabase,System.Threading.Tasks.Task>' does not contain a definition for 'Verifiable' and no extension method 'Verifiable' accepting a first argument of type 'Moq.Language.Flow.IReturnsThrows' could be found (are you missing a using directive or an assembly reference?)

If I change db.HashSetAsync to db.HashSet in the Setup invocation, it works as expected. It appears that the setting a Callback on a regular method returns an ICallbackResult but setting a callback on an async method invocation returns an IReturnsThrows - and I'm not sure how you mark one of those as verifiable. Any ideas?

Dylan Beattie
  • 53,688
  • 35
  • 128
  • 197

1 Answers1

11

For async methods you need to return a completed Task from the setup before using a callback

have a look here:

Using Moq to mock an asynchronous method for a unit test

You're creating a task but never starting it, so it's never completing. However, don't just start the task - instead, change to using Task.FromResult<TResult> which will give you a task which has already completed:

this works

const string KEY = "some_key";
var db = new Mock<IDatabase>();
db.Setup(d => d.HashSetAsync(KEY, It.IsAny<HashEntry[]>(), It.IsAny<CommandFlags>()))
    .Returns(Task.FromResult<object>(null))
    .Callback<RedisKey, HashEntry[], CommandFlags>((key, hash, flags) => {
        hash.ShouldContain(entry => entry.Name == "customerid");
        hash.ShouldContain(entry => entry.Name == "quotenumber");
    })
    .Verifiable();
Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472