我需要对一个大致如下所示的方法进行单元测试:
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
可能要等待十几个任务。然后,我必须编写一打测试,每个测试都会有一个不同的单个任务引发异常,以确保所有测试均正确传播。编写一堆相同的测试听起来不是一件好事。如果我可以断言所有任务都已等待-这将解决我的问题,因为我可以相信异步框架可以在发生错误时进行传播。
答案 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"}.
}