我使用resharper和visual studio test runner在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
关于如何从特定行测试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;
}
答案 0 :(得分:10)
我假设您要检查LongRunningFunction
是否会抛出TaskCanceledException
。
我认为你所经历的行为是完全正确的,误解就在这句话中:
await LongRunningFunction(token)
在这里,您正在有效地执行异步操作并等待它完成,这也将重新抛出其调用中发生的第一个异常。您基本上可以用以下内容替换它:
throw new TaskCanceledException()
因此,为什么前两个测试成功 - 你正在使用ExpectedExceptionAttribute
- 第三个失败 - 你不期待异常。
Assert.That
的第一个参数,当你稍后使用Throws
时,应该是某种类型的委托,因为NUnit必须调用它才能捕获从它冒出的异常调用。如果您自己调用它,除了使用ExpectedExceptionAttribute
之外,NUnit当然无法捕获异常。
换句话说,理想的正确方法是:
// WARNING: this code does not work in NUnit <= 2.6.2
Assert.That(async () => await LongRunningFunction(token), Throws.InstanceOf<TaskCanceledException>());
我想告诉你,NUnit支持异步方法的这种语法,这非常自然,允许你在代码的特定部分测试异常,但它没有,测试会报告失败您期待异常但没有发生异常。
原因是为了从异步匿名方法的调用中获取异常,NUnit必须等待它,而它目前没有。
我可以给你的另一个选择是使用非同步lambda,你{as}操作返回的任务Wait
,但遗憾的是语法不太好,因为等待异步操作的行为与等待返回的任务的行为方式不同。具体来说,在异步操作抛出异常的情况下,您将在第一种情况下获得实际异常,在第二种情况下获得AggregateException
。在任何情况下,这里都有一些适用于2.6.2的代码:
var aggregate = Assert.Throws<AggregateException>(() => LongRunningFunction(token).Wait());
Assert.IsInstanceOf<TaskCanceledException>(aggregate.InnerExceptions.Single());
总而言之,尽管NUnit 2.6.2确实引入了对允许编写async [void|Task|Task<T>]
测试的异步测试方法的支持,但我们没有考虑将支持扩展到异步匿名方法,这些方法对此有用。虽然我相信我们可以做出这样的断言。
答案 1 :(得分:0)
如果将Resharper 7.1或更高版本与NUnit 2.6.2或更高版本一起使用,则public async void
的测试方法将起作用。 Resharper 7.1今天(2012年11月13日)发布
早期版本的Resharper测试运行器不会等待测试完成,并且可以在没有实际测试的情况下通过。
2.6.2之前的NUnit版本存在同样的问题。
Resharper NUnit测试运行器与NUnit GUI和命令行测试运行器的代码库不同,并由Jetbrains单独更新。
答案 2 :(得分:-2)
Peter Unfortuantely,这似乎没有按预期工作。第一个测试AsyncTaskCanceledSemiWorking只能工作,因为我有expectedexception属性。实际的断言完全被忽略(正如您可以通过ArgumentOutOfRange异常看到的那样,这只是假的失败)。
期望是错误的,因为该属性优先于Assert.Throws。
正如Simone已经证明的那样,期望是“断言”异常是InstanceOf,因此,它属于Assertion而不是抛出。我认为您的期望可能基于在Java中使用Throws的方式。