我试图模拟单元测试的复杂情况:
_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
关键字:测试的执行在{{1}之前继续完成(尽管回调中有Task
)。这种情况的一个症状是在await
await Task.Delay(1000)
参数中添加Interrupt()
会将通过的测试转变为失败的测试。
这种行为是否是由于测试期间如何处理线程或Task
的细微差别?或Moq的限制?我的测试方法有这个签名:
Task
答案 0 :(得分:7)
Callback
无法返回值,因此不应该用于执行异步代码(或需要返回值的同步代码)。 Returns
是一种注射点&#34;您可以挂钩检查传递到方法的参数,但不能修改它返回的内容。
如果你想要一个lambda模拟,你可以使用_mockController.Setup(tc => tc.Interrupt(It.IsAny<Func<Task>>()))
.Returns(async f => await f());
:
Interrupt
(我假设Task
返回Callback
)。
在任务完成之前继续执行测试(尽管在回调中等待)。
是的,由于Action
无法返回值,因此始终键入Action<...>
/ async
,因此您的async void
lambda最终是一个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
方法,all the problems that brings(如我的MSDN文章中所述)。
<强>更新强>
Interrupt()实际上是一个void方法:它的作用是对函数(参数)进行排队,直到IController可能会被打扰停止它正在做的事情。然后它调用函数 - 它返回一个任务 - 并等待该任务
如果可以的话,你可以模仿那种行为:
<label for="x">x: </label><input id="x" type="number" placeholder="0"/>
<label for="y">y: </label><input id="y" type="number" placeholder="0"/>
<label for="z">z: </label><input id="z" type="number" placeholder="0"/>
<div id="calculations">
<button data-calc-type="area">Pole</button>
<button data-calc-type="circuit">Obwód</button>
<button data-calc-type="volume">Objetość</button>
</div>
<p>Wynik: <span id="result"></span></p>