我正在尝试为单元测试模拟一个复杂的情况:
_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/