c# - 模拟对象上的异步回调不等待

标签 c# unit-testing mocking async-await moq

我正在尝试为单元测试模拟一个复杂的情况:

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

在哪里IController有一个 void 方法 Interrupt(Func<Task>> f) ,它将一些待完成的工作排队。

我的被测对象确实调用了 Interrupt() ,我可以像这样验证调用:

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

...但是当参数Func<Task>在回调中被调用,await Task 中不尊重关键字:测试的执行在 Task 之前继续完成(尽管回调中的 await)。一个症状是添加 await Task.Delay(1000)Interrupt() Task参数将通过的测试变成失败的测试。

这种行为是由于线程或 Task 的细微差别造成的吗? s 在测试期间被处理?还是最小起订量的限制?我的测试方法有这个签名:

[Test]
public async Task Test_Name()
{
}

最佳答案

Callback不能返回值,因此不应用于执行异步代码(或需要返回值的同步代码)。 Callback是一种“注入(inject)点”,您可以 Hook 它以检查传递给方法的参数,但不能修改它返回的内容。

如果你想要一个 lambda mock,你可以使用 Returns :

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

(我假设 Interrupt 返回 Task )。

the execution of the test continues before the Task finishes (despite the await in the callback).

是的,因为 Callback不能返回值,它总是输入为 Action/Action<...> , 所以你的 async lambda 最终成为 async void方法,用 all the problems that brings (如我的 MSDN 文章中所述)。

更新:

Interrupt() is actually a void method: what it does is queue the function (the argument) until the IController can be bothered to stop what it is doing. Then it invokes the function--which returns a Task--and awaits that Task

然后你可以模拟那个行为,如果这可行的话:

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

关于c# - 模拟对象上的异步回调不等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36583780/

相关文章:

c# - 使用 Twitter Bootstrap 在 ASP.NET MVC 中调用模态对话框的最佳方式是什么?

c# - AddEventHandler 使用反射

c# - 单元测试 openGl 显示

java - 是否可以使测试方法参数化,而不是整个类?

c# - 模拟 HttpPostedFileWrapper

javascript - 我可以在编写 TypeScript 单元测试时对依赖项进行猴子补丁吗

c# - 未在接口(interface)中定义的属性的 setter

c# - 是否有更简洁的方法将分隔文本拆分为数据结构?

c# - 是否可以将方法限制为仅在友好程序集中使用虚拟方法?

java - 模拟的模拟 Actor