Asynchronous callback on a mocking object not waiting

I am trying to mock a difficult situation for unit testing:

_mockController = new Mock<IController>(); _mockController.Setup(tc => tc.Interrupt(It.IsAny<Func<Task>>())) .Callback<Func<Task>>(async f => await f.Invoke()); 

Where IController has a void Interrupt(Func<Task>> f) method that queues up some work that needs to be done.

My objects under testing call Interrupt() , and I can test the call as follows:

 _mockController.Verify(tc => tc.Interrupt(It.IsAny<Func<Task>>()), Times.Once); 

... but when the Func<Task> argument is called in the callback, the await keyword is not respected in the Task : the test continues until the Task finishes (despite the callback). One symptom of this is that adding await Task.Delay(1000) in the Interrupt() Task argument turns the passing test into a failed test.

Is this behavior due to threads being processed or Task ? Or a Moka restriction? My testing methods have the following signature:

 [Test] public async Task Test_Name() { } 
+6
source share
1 answer

Callback cannot return a value and, therefore, should not be used to execute asynchronous code (or synchronous code to which a value must be returned). Callback is a kind of โ€œinjection pointโ€ that you can connect to check the parameters passed to the method, but not change what it returns.

If you want a lambda mock, you can just use Returns :

 _mockController.Setup(tc => tc.Interrupt(It.IsAny<Func<Task>>())) .Returns(async f => await f()); 

(I assume Interrupt returns a Task ).

test execution continues until the task is completed (despite waiting in the callback).

Yes, since Callback cannot return a value, it is always typed as Action / Action<...> , so your async lambda ends with the async void method with all the problems that arise (as described in my MSDN article).

Update:

Interruption () is actually a void method: what it does is a queue in a function (argument) until IController can stop what it does. He then calls a function - which returns the task - and expects this task

You can make fun of this behavior if it works:

 List<Func<Task>> queue = new List<Func<Task>>(); _mockController.Setup(tc => tc.Interrupt(It.IsAny<Func<Task>>())) .Callback<Func<Task>>(f => queue.Add(f)); ... // Code that calls Interrupt // Start all queued tasks and wait for them to complete. await Task.WhenAll(queue.Select(f => f())); ... // Assert / Verify 
+6
source

All Articles