在单元测试中同步调用异步方法是不是不正确?

Posted

技术标签:

【中文标题】在单元测试中同步调用异步方法是不是不正确?【英文标题】:Is it incorrect to call async methods synchronously in unit tests?在单元测试中同步调用异步方法是否不正确? 【发布时间】:2015-08-12 21:51:47 【问题描述】:

我是 async/await 领域的新手,我正在尝试找出为异步方法编写异步单元测试的好处。也就是说,必须对异步方法进行单元测试以异步调用该异步方法吗?如果它使用Task.Run()同步调用异步方法,会丢失什么?在后一种情况下,就我所见,代码覆盖率没有受到影响。

我问这个的原因是因为我们的模拟软件(我们使用 TypeMock)不能支持 async/await。 (They say there is a legitimate reason 缺乏支持,我并不反对他们。)通过在单元测试中同步调用异步方法,我们可以解决这个问题。不过,我想知道我们这样做是否是在偷工减料。

例如,假设我有以下异步方法:

public async Task<string> GetContentAsync(string source)

    string result = "";
    // perform magical async IO bound work here to populate result
    return result;

以下是不起作用的理想单元测试:

[TestMethod()]
public async Task GetContentAsyncTest()

    string expected = "thisworks";
    var worker = new Worker();
    // ...mocking code here that doesn't work!
    string actual = await worker.GetContentAsync();
    Assert.AreEqual(expected, actual);

但这有效,并且确实提供了我们需要的代码覆盖率。这样可以吗?

[TestMethod()]
public void GetContentAsyncTest()

    string expected = "thisworks";
    var worker = new Worker();
    // mocking code here that works!
    string actual = Task.Run(() => worker.GetContentAsync()).Result;
    Assert.AreEqual(expected, actual);

【问题讨论】:

我不太喜欢这个问题;标题有点宽泛。在大多数情况下,您不是在测试某件事是否花费了 X 长或 Y 长,您只是在说“如果它完成了,请确保这发生了”或“如果它失败了,请确保这发生了”。在绝大多数情况下,是否是异步都不会发挥作用。 异步处理主要是为了释放线程池或者IIS工作进程 @George Stocker 我知道,但我看到异步单元测试示例的事实让我认为人们编写它们是有原因的。那你对标题有什么建议?你不能在投反对票之前给我一个改进它的机会吗?有点触发快乐? 还有TypeMock的原因不好。 @Zoomzoom 嗯...不是真的。异步就像毒药,我们只是将堆栈中的所有内容都设为异步:p 【参考方案1】:

必须对异步方法调用的异步方法进行单元测试吗?

不,但这样做是最自然的。

如果使用Task.Run()同步调用async方法,会丢失什么?

真的没什么。它的性能稍差一些,但在某种程度上你可能永远不会注意到。

您可能希望在失败测试中使用 GetAwaiter().GetResult() 而不是 Result 以避免 AggregateException 包装器。您也可以直接调用该方法;无需将其包装在 Task.Run 中。

他们说缺乏支持是有正当理由的,我不同意他们的观点。

哦,我当然不同意他们的观点。 :)

这是否意味着他们也不能对迭代器块进行单元测试?完全相同的推理将适用......


不支持async 单元测试的唯一更严重的问题是,如果被测代码假定其上下文将处理同步。这很常见,例如,在中等复杂的视图模型中。

在这种情况下,您需要安装一个上下文来执行 async 代码(例如,我的 AsyncContext type),除非您使用的单元测试框架会自动提供一个(在撰写本文时) ,只有 xUnit 会 AFAIK)。

【讨论】:

关于性能损失的第二点 - 即使它很重要,对于单元测试来说仍然完全不是问题。 @Zoomzoom:我不同意。在某种程度上,performance of unit tests is important。 斯蒂芬-我明白了。很难与之争辩!但就我的情况而言,测试性能远没有代码覆盖率那么重要。 @Zoomzoom 小心过分强调代码覆盖率。有时它远没有人们想象的那么重要***.com/questions/90002/…【参考方案2】:

如果您使用 xUnit 而不是 MSTest,您的理想解决方案(异步测试)将起作用。

【讨论】:

我正在使用 xUnit,但是在测试异步方法时我遇到了奇怪的死锁。我什至尝试过 ConfigureAwait(false)。

以上是关于在单元测试中同步调用异步方法是不是不正确?的主要内容,如果未能解决你的问题,请参考以下文章

如何对带有异步的流进行单元测试

如何在单元测试之前等待组件的挂载回调中的异步调用完成?

服务器调用的完成和错误块的单元测试

vue 单元测试入门

单元测试在C#,Moq中调用SAP异步Web服务

使用 Moq 模拟单元测试的异步方法