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;
    } 
4

3 回答 3

10

我假设您想检查是否LongRunningFunction会抛出TaskCanceledException.

我认为您所遇到的行为是完全正确的,而误解就在这句话中:

await LongRunningFunction(token)

在这里,您正在有效地执行异步操作并等待它完成,这也将重新引发在其调用中发生的第一个异常。您基本上可以将其替换为:

throw new TaskCanceledException()

因此,为什么前两个测试成功 - 你正在使用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>());

我想告诉你,NUnit 支持异步方法的这种语法,这很自然,允许你在代码的特定部分测试异常,但它不支持,并且测试会失败,报告你是期待异常但没有发生异常。

原因是为了从该异步匿名方法的调用中获取异常,NUnit 必须等待它,而它目前没有。

我可以为您提供的一种替代方法是在异步操作返回的任务上使用非异步 lambda 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>]测试,但我们没有考虑将支持扩展到对这种断言有用的异步匿名方法,尽管我相信我们可以。

于 2012-10-24T21:31:17.607 回答
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 单独更新。

于 2012-11-13T21:53:32.260 回答
-2

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

期望是错误的,因为属性优先于 Assert.Throws。

正如 Simone 所展示的,期望是“断言”异常是 InstanceOf,因此它属于 Assertion 而不是 throws。我认为您的期望可能是基于 Throws 在 Java 中的使用方式。

于 2012-10-25T09:36:06.300 回答