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

本文关键字:是否 不正确 异步方法 调用 单元测试 同步 | 更新日期: 2023-09-27 17:51:13

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

我问这个问题的原因是因为我们的模拟软件(我们使用TypeMock)不支持async/await。(他们说缺乏支持是有正当理由的,我不反对他们的观点。)通过在单元测试中同步调用异步方法,我们可以解决这个问题。但是,我想知道我们这样做是否走捷径。

例如,假设我有以下异步方法:
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);
}

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

异步方法的单元测试必须异步调用该异步方法吗?

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

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

没什么。它的性能稍微差一些,但你可能永远不会注意到。

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

他们说缺乏支持有合理的原因,我不反对他们。

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

这是否意味着他们也不能对迭代器块进行单元测试?同样的推理也适用于…


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

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

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