I am attempting to mock a complicated situation:
_mockController = new Mock<IController>();
_mockController.Setup(c => c.Interrupt(It.IsAny<Func<Task>>()))
.Callback<Func<Task>>(async f => await f.Invoke());
Where IController has a method Interrupt(Func<Task>> f). Although the real behavior is more involved, I am happy to just have this mock immediately invoke the parameter and await the Task.
My objects under test do call Interrupt(), and I can verify the call like so:
_mockController.Verify(c => c.Interrupt(It.IsAny<Func<Task>>()), Times.Once);
...but when the argument Func<Task> is invoked in the callback, the await keyword is not respected in the Task: the execution of the test continues before the Task finishes (despite the await in the callback). One symptom of this is that adding an await Task.Delay(1000) in the Interrupt() Task argument can turn a passing test into a failing test without any changes to logic.
Could this be a limitation of Moq? Is there a way to use Moq for this test? My test methods have this signature, using NUnit:
[Test]
public async Task Test_Name()
{ ... }
As you can read in the Stack Overflow question, Callback() returns void and therefore is not awaitable. A fix for this would be an AsyncCallback() method that returns Task.
For anyone else interested in the same point, we worked around (for now) the lack of a Task returning CallbackAsync() method in moq, by moving the code we wanted to await in Moq's Callback(), simply into a .Returns(...) portion of the Moq .Setup(...). Example.
Of course, this may only help in situations where the method being .Setup(...)'d has a return value, not void.
The real problem here is that Interrupt wants to await something, but has a void return type (as has been stated by others). So this is more of a design problem in the user code than a limitation on Moq's part. It should be easy to simply use Returns instead.
But setting aside the particular case from the original poster with Interrupt .... could a CallbackAsync(...) method not still be useful in Moq more generally? There's some asymmetry in the Moq API at the moment. One can ReturnsAsync(), but not CallbackAsync(), for example.
EDIT: And because you can put .CallBack(...).ReturnsAsync(...) chained like that, as in this original post, there is perhaps some expectation that the .CallBack(...) might run asynchronously (or there might be an asynchronous version).
The approach in #384 looked promising! (Apologies that I cannot offer to help with this right now - my outside-paid-work time is occupied running Polly)
BTW, Moq is a fantastic library! Thank you for everything you do!
[C]ould a
CallbackAsync(...)method not still be useful in Moq more generally?
Possibly, yes.
The approach in #384 looked promising!
Agreed, and it's high time I get back on that issue. I guess if we're going to decide that CallbackAsync really makes sense and should be added after all, that would likely be the issue where it'll happen.
(I closed this issue here because I understood it to be more of a question than a feature request.)
Most helpful comment
Possibly, yes.
Agreed, and it's high time I get back on that issue. I guess if we're going to decide that
CallbackAsyncreally makes sense and should be added after all, that would likely be the issue where it'll happen.(I closed this issue here because I understood it to be more of a question than a feature request.)