无法等待

时间:2016-04-12 20:49:16

标签: 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关键字:测试的执行在{{1}之前继续完成(尽管回调中有Task)。这种情况的一个症状是在await await Task.Delay(1000)参数中添加Interrupt()会将通过的测试转变为失败的测试。

这种行为是否是由于测试期间如何处理线程或Task的细微差别?或Moq的限制?我的测试方法有这个签名:

Task

1 个答案:

答案 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>