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

Is it incorrect to call async methods synchronously in unit tests?

我是 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);
}

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

must a unit test for an async method call that async method asynchronously?

没有,但这样做是最自然的。

If it calls the async method synchronously using Task.Run(), what is lost?

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

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

They say there is a legitimate reason for this lack of support, and I don't disagree with them.

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

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


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

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