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

时间:2015-05-29 21:46:49

标签: c# unit-testing asynchronous async-await typemock-isolator

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

2 个答案:

答案 0 :(得分:3)

  

异步方法的单元测试是否必须异步调用async方法?

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

  

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

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

您可能希望使用GetAwaiter().GetResult()代替Result来避免失败测试中的AggregateException包装。你也可以直接调用这个方法;无需将其包装在Task.Run

  

他们说这种缺乏支持是有正当理由的,我并不反对他们。

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

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

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

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

答案 1 :(得分:2)

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