Nunit: Exception thrown in async void EventHandler is not caught by Nunit

Created on 17 Aug 2017  路  2Comments  路  Source: nunit/nunit

When exceptions are thrown from within an async EventHandler invocation, the exception is not caught by Nunit, Visual Studio 2017.3 shows this in the Tests output window

The active test run was aborted. Reason: Unhandled Exception: System.Exception: Exception of type 'System.Exception' was thrown.
   at NunitTest.MyEventClass.<>c.<<-ctor>b__1_0>d.MoveNext() in c:\users\nzsma\documents\visual studio 2017\Projects\NunitTest\NunitTest\Program.cs:line 16
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

The following example is built with .Net Core 1.1

```C#
using System;
using System.Threading.Tasks;
using NUnit.Framework;

namespace NunitTest
{
public class MyEventClass
{
public EventHandler MyEvent;

    public MyEventClass()
    {
        MyEvent += async (s, a) =>
        {
            await Task.Yield();
            throw new Exception();
        };
    }

    public void TriggerEvent()
    {
        MyEvent(this, new EventArgs());
    }
}

[TestFixture]
public class TestMyClass
{
    [Test]
    public void Test1() // This test on it's own, will pass
    {
        Assert.That(async () =>
        {
            await Task.Yield();
            throw new Exception();
        }, Throws.TypeOf<Exception>());
    }

    [Test]
    public void Test2() // When this test runs, ALL RESULTs are inconclusive
    {
        Assert.That(() =>
        {
            var myEvent = new MyEventClass();

            myEvent.TriggerEvent();
        }, Throws.TypeOf<Exception>());
    }
}

}
```

question

All 2 comments

This is because async void is fire and forget. In other words, as NUnit is executing the code, the event handler is scheduled for execution some time in the future, but NUnit cannot await it because their is no Task to await. Because NUnit cannot await it, NUnit is likely done running the test method when the exception is thrown.

For Test1 we know that you are passing in an async void method, so we wrap it in an async invocation region, but we can't do that with Test2 because the async void call is buried within your code. Notice your Asset.That isn't even async.

I would suggest not trying to test async void methods. When I run into this, I move the body of the async void method out to an async Task method and test that, leaving the events untested.

```C#
public MyEventClass()
{
MyEvent += async (s, a) => await PerformEvent();
}

    async Task PerformEvent()
    {
        await Task.Yield();
        throw new Exception();
    }

```
I am going to close this as a question because what you are trying to do is unsupported. If you have question, feel free to post, we will see them.

@rprouse is exactly right. Assert.That(..., Throws can only detect exceptions that are catchable in the call stack, no different than if you had written this using try...catch. By definition, exceptions thrown after an await in an async void method are uncatchable by the caller. They can only be handled via an event like AppDomain.UnhandledException but that provides no context to correlate them to a certain test.

Was this page helpful?
0 / 5 - 0 ratings