Pet*_*ter 2 c# nunit unit-testing async-await
我使用resharper和visual studio测试运行器在Visual Studio 2012中使用最新版本的NUnit(2.6.2).我有以下示例测试,我试图验证在预期的异步方法调用上引发异常.
不幸的是,这似乎没有按预期工作.第一个测试AsyncTaskCanceledSemiWorking只有因为我有expectedexception属性.实际的断言完全被忽略(正如您可以通过ArgumentOutOfRange异常看到的那样,这只是一个让它失败的假装).
该AsyncTaskCanceledWorking精品工程,但犯规测试抛出异常的某一行,因此不太有用.
第三个与下面的庄严失败....
System.Threading.Tasks.TaskCanceledException : A task was canceled.
Exception doesn't have a stacktrace
Run Code Online (Sandbox Code Playgroud)
关于如何从特定行测试TaskCanceledException的任何想法都非常有用.
谢谢
[Test]
[ExpectedException(typeof(TaskCanceledException))]
public async Task AsyncTaskCanceledSemiWorking()
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
cancellationTokenSource.Cancel();
Assert.That(await LongRunningFunction(token), Throws.InstanceOf<ArgumentOutOfRangeException>());
}
[Test]
[ExpectedException(typeof(TaskCanceledException))]
public async Task AsyncTaskCanceledWorking()
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
cancellationTokenSource.Cancel();
int i = await LongRunningFunction(token);
}
[Test]
public async Task AsyncTaskCanceledFailed()
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
cancellationTokenSource.Cancel();
Assert.That(await LongRunningFunction(token), Throws.InstanceOf<TaskCanceledException>());
}
public async Task<int> LongRunningFunction(CancellationToken token)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1000, token);
return 5;
}
Run Code Online (Sandbox Code Playgroud)
Sim*_*one 10
我假设你想检查一下LongRunningFunction会抛出一个TaskCanceledException.
我认为你所经历的行为是完全正确的,误解在于这句话:
await LongRunningFunction(token)
Run Code Online (Sandbox Code Playgroud)
在这里,您有效地执行异步操作并等待它完成,这也将重新抛出其调用中发生的第一个异常.您基本上可以用以下内容替换:
throw new TaskCanceledException()
Run Code Online (Sandbox Code Playgroud)
因此,为什么前两个测试成功 - 你正在使用ExpectedExceptionAttribute- 而第三个失败 - 你不期待异常.
Assert.That当您稍后使用时Throws,第一个参数应该是某种类型的委托,因为NUnit必须调用它才能捕获从其调用中冒出的异常.如果你自己调用它,当然除了使用之外,NUnit无法捕获异常ExpectedExceptionAttribute.
换句话说,理想的正确方法是:
// WARNING: this code does not work in NUnit <= 2.6.2
Assert.That(async () => await LongRunningFunction(token), Throws.InstanceOf<TaskCanceledException>());
Run Code Online (Sandbox Code Playgroud)
我想告诉你,NUnit支持异步方法的这种语法,这是非常自然的,允许你在代码的特定部分测试异常,但它没有,并且测试将无法报告你是期待例外,但没有发生异常.
原因是为了从异步匿名方法的调用中获取异常,NUnit必须等待它,它目前没有.
我可以给你的一个替代方案是使用非异步lambda Wait来执行异步操作返回的任务,但遗憾的是语法不太好,因为等待异步操作的行为方式与等待任务不同回报.具体来说,在异步操作抛出异常的情况下,您将在第一种情况下获得实际异常,AggregateException在第二种情况下获得实际异常.在任何情况下,这里都有一些适用于2.6.2的代码:
var aggregate = Assert.Throws<AggregateException>(() => LongRunningFunction(token).Wait());
Assert.IsInstanceOf<TaskCanceledException>(aggregate.InnerExceptions.Single());
Run Code Online (Sandbox Code Playgroud)
总而言之,尽管NUnit 2.6.2确实引入了对允许编写async [void|Task|Task<T>]测试的异步测试方法的支持,但我们并没有考虑将支持扩展到异步匿名方法,这种方法在这种断言中很有用,尽管我相信我们可以.