异步单元测试未按预期工作

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>]测试的异步测试方法的支持,但我们并没有考虑将支持扩展到异步匿名方法,这种方法在这种断言中很有用,尽管我相信我们可以.