2012-10-24 74 views
2

我在Visual Studio 2012中使用最新版本的NUnit(2.6.2),使用了resharper和visual studio测试运行器。我有以下示例测试,其中我试图验证在预期的异步方法调用中引发异常。异步单元测试不能按预期工作

不幸的是,这似乎没有按预期工作。第一个测试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

嗨,彼得,这将有助于知道你会期望什么行为,而不是。 – Simone

回答

10

我假设你要检查LongRunningFunction将抛出一个TaskCanceledException

我认为您所遇到的行为是完全正确的,误会是在此声明:

await LongRunningFunction(token) 

在这里,你得到有效执行异步操作,并等待它完成,这也将重新抛出调用中发生的第一个异常。你基本上可以替换成:

throw new TaskCanceledException() 

因此,为什么前两个测试是成功的 - 您使用的是ExpectedExceptionAttribute - 第三失败 - 你没有预期的异常。

当你以后使用Throws时,Assert.That的第一个参数应该是某种类型的委托,因为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将不得不等待它,它目前没有。

一个替代方案,我可以给你是使用非异步拉姆达,你Wait由异步操作返回的任务,但语法是不幸的是没有好的,因为等待异步操作在不同的行为从方式等待它返回的任务。特别是,在异步操作引发异常的情况下,您会在第一种情况下得到实际的异常,而在第二种情况下会得到实际的异常。在任何情况下,这里的一些代码,将与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>]测试中,我们没有考虑将支持扩展到异步匿名方法,尽管我相信我们可以这样做,但这对于这种断言很有用。

+0

我想等待一个抛出异常的方法,并声明抛出异常。我相信这就是你说我实际上需要一个委托的地方。你认为NUnit很快就能支持这一点吗?感谢您的回复 – Peter

+0

我无法确定地告诉您,我只能说这是可能的。请允许我指出你的第一个陈述是矛盾的。如果您等待某个操作,则无法断言它会引发异常,除非您使用可以捕获该异常的代码封装该调用,否则该异常就会冒泡,这就是您的示例中发生的情况。 – Simone

-2

彼得不幸,这似乎没有按预期工作。第一次测试AsyncTaskCanceledSemiWorking只能工作,因为我有expectedexception属性。实际的断言是完全被忽略的(正如你可以通过ArgumentOutOfRange异常看到的那样,这只是一个假的让它失败)。

期望是错误的,因为该属性优先于Assert.Throws。正如Simone所示,期望是“断言”该异常是一个InstanceOf,因此它属于Assertion而不是抛出。我认为你的期望可能是基于Java中使用Throws的方式。

+0

Andrew,你是什么意思,编辑我这样的评论? –

+0

安德鲁,请不要干涉适度的名称,因为您完全误导了帖子。 –

+0

安德鲁,它似乎被浏览器吃掉的HTML标签用法(“小于”和“大于”)让我认为它是由您编辑的。对于先前的评论感到抱歉(试图删除我的评论,但没有规定)。 –

0

如果您在NUnit 2.6.2或更高版本中使用Resharper 7.1或更高版本,则可以使用public async void的测试方法。 Resharper 7.1今天发布(2012年11月13日)

早期版本的Resharper测试运行器不会等待测试完成,并且可以在没有实际测试的情况下通过测试。

2.6.2之前的NUnit版本有很多相同的问题。

Resharper NUnit测试运行器与NUnit GUI和命令行测试运行器不是相同的代码库,并且由Jetbrains单独更新。