如何为async / await方法编写单元测试,我正在使用Visual Studio 2013。
假设我们有一个异步方法:
public async Task DoSomethingAsync()
{
...
await _service.DoInternalAsync();
...
}
由于我使用的是最新版本的Visual Studio,因此它对异步方法单元测试有很好的支持:
[TestMethod]
public async Task DoSomthingAsyncTest()
{
...
await _objectUnderTest.DoSomethingAsync();
// how to verify the result??? here is what I did
_service.Verify(_ => _.DoInternalAsync());
}
基本上,我有两个问题:
Task
结果?我这样做了吗?await _service.DoInternalAsync()
句子,从代码覆盖率结果的角度来看,它提示MoveNext()
句子有6个未覆盖的块。它内有什么问题?答案 0 :(得分:8)
好的,根据我的研究,代码覆盖率问题是最新版本Visual Studio 2013中的Visual Studio错误,他们会在下一个主要版本中修复/增强它。
来自feedback:
的引用您所看到的问题是由于我们最终的错误导致我们尚未完全支持代码覆盖中的async / await模式。这项工作尚未完成,应该是我们在下一次重大更新/发布中提供的内容。这个问题没有干净的解决方法。
答案 1 :(得分:2)
代码未显示为覆盖的原因与异步方法的实现方式有关。 C#编译器实际上将异步方法中的代码转换为实现状态机的类,并将原始方法转换为初始化并调用该状态机的存根。由于此代码是在程序集中生成的,因此它包含在代码覆盖率分析中。
如果在正在执行的代码执行时使用未完成的任务,则编译器生成的状态机将挂起完成回调以在任务完成时恢复。这样可以更完整地运行状态机代码,从而实现完整的代码覆盖(至少对于语句级代码覆盖工具而言)。
获取当前未完成但在某个时刻完成的任务的常用方法是在单元测试中使用Task.Delay。但是,这通常是一个糟糕的选择,因为时间延迟太小(并导致不可预测的代码覆盖率,因为有时任务在测试运行的代码之前完成)或太大(不必要地减慢测试速度)。
更好的选择是使用"等待Task.Yield()"。这将立即返回,但一旦设置就会立即调用。
另一种选择 - 尽管有些荒谬 - 是实现你自己的等待模式,它具有报告的语义不完整,直到连接回调被连接,然后立即完成。这基本上迫使状态机进入异步路径,提供完整的覆盖。
可以肯定的是,这不是一个完美的解决方案。最不幸的是,它需要修改生产代码以解决工具的限制。我更希望代码覆盖工具忽略编译器生成的异步状态机部分。但是在这种情况发生之前,如果您真的想要尝试获得完整的代码覆盖率,则没有太多选择。
可以在此处找到有关此黑客的更完整说明: http://blogs.msdn.com/b/dwayneneed/archive/2014/11/17/code-coverage-with-async-await.aspx