Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async callbacks do not respect async/await #256

Closed
Jdsleppy opened this issue Apr 13, 2016 · 6 comments
Closed

Async callbacks do not respect async/await #256

Jdsleppy opened this issue Apr 13, 2016 · 6 comments

Comments

@Jdsleppy
Copy link

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()
{ ... }
@StephenCleary
Copy link

(This question has been posted on SO)

@Jdsleppy
Copy link
Author

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.

@reisenberger
Copy link

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.

@stakx
Copy link
Contributor

stakx commented Nov 12, 2017

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.

@stakx stakx closed this as completed Nov 12, 2017
@reisenberger
Copy link

reisenberger commented Nov 12, 2017

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!

@stakx
Copy link
Contributor

stakx commented Nov 12, 2017

[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.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants