如何对异步任务等待进行单元测试?

时间:2018-08-06 10:24:57

标签: c# unit-testing asynchronous

我需要对一个大致如下所示的方法进行单元测试:

public async Task DoStuffAsync()
{
    var tasks = { dependency.FooAsync(), dependency.BarAsync() };

    await Task.WhenAll(tasks);
}

我的第一种方法(使用Moq)是这样的:

dependency.Setup(d => d.FooAsync()).Returns(Task.FromResult(default(object)));
dependency.Setup(d => d.BarAsync()).Returns(Task.FromResult(default(object)));

await systemUnderTest.DoStuffAsync();

dependency.Verify(d => d.FooAsync(), Times.Once);
dependency.Verify(d => d.BarAsync(), Times.Once);

但这不会,因为如果我将方法更改为类似以下这样的愚蠢的话:

public async Task DoStuffAsync()
{
    var tasks = { dependency.FooAsync(), dependency.BarAsync() };

    await tasks[0];
}

测试不会失败。我如何断言任务已经等待?

编辑:

我认为这个问题的解决方案可能类似于我的: 假设我要测试此方法是否失败,即其中一项任务抛出,并且我想断言该异常已传播。我需要检查所有任务,但是Task.WhenAll可能要等待十几个任务。然后,我必须编写一打测试,每个测试都会有一个不同的单个任务引发异常,以确保所有测试均正确传播。编写一堆相同的测试听起来不是一件好事。如果我可以断言所有任务都已等待-这将解决我的问题,因为我可以相信异步框架可以在发生错误时进行传播。

1 个答案:

答案 0 :(得分:1)

您给出的上下文很少(编程完全是关于上下文的:))。
在您的特殊情况下,如果您想检查DoStuffAsync等待所有依赖项返回的任务都抛出异常并等待它们

对于.NET 4.5.1,创建帮助器方法以创建带有异常的任务。

static Task CreateTaskWithException(string exceptionMessage) 
{
    var taskSource = new TaskCompletionSource<object>();
    var exception = new Exception(exceptionMessage);

    taskSource.SetException(exception);

    return taskSource.Task;
}

如果您有多个依赖项,则可以测试等待-您可以一次简单地测试所有依赖项

dependency.Setup(d => d.FooAsync()).Returns(CreateTaskWithException("Foo"));
dependency.Setup(d => d.BarAsync()).Returns(CreateTaskWithException("Bar"));
dependency.Setup(d => d.FizzBuzzAsync()).Returns(CreateTaskWithException("FizzBuzz"));

var expectedErrors = new[] { "Foo", "Bar", "FizzBuzz" };
string[] actualErrors = null;
try
{
    DoStuffAsync().Wait();
    Assert.Fail("DoStuffAsync should throw exception");
}
catch(AggregateException exception)
{
    actualErrors = exception.InnerExceptions.Select(ex => ex.Message);
}

actualErrors.Should().BeEquivalentTo(expected);

如果不等待所有任务,测试将失败。

public async Task DoStuffAsync()
{
    var tasks = { dependency.FooAsync(), dependency.BarAsync() };
    reutrn Task.WhenAll(tasks.Skip(1));
    // Test fail with message:
    // Expected subject to be a collection with 2 item(s), but {"Bar"}
    // contains 1 item(s) less than {"Foo", "Bar"}.        
}