Handle Exceptions from async EventHandler with Nunit tests
using System; | |
using System.Collections.Generic; | |
using System.Threading.Tasks; | |
using NUnit.Framework; | |
namespace NunitTest | |
{ | |
public delegate Task AsyncEventHandler(object sender, EventArgs e); | |
public class MyEventClass | |
{ | |
/// <summary> | |
/// A async event delegate | |
/// </summary> | |
public AsyncEventHandler MyAsyncEvent; | |
/// <summary> | |
/// A typical event delegate | |
/// </summary> | |
public EventHandler MyEvent; | |
public MyEventClass() | |
{ | |
// Add a async lambda that throws an exception | |
MyAsyncEvent += async (s, a) => | |
{ | |
await Task.Yield(); | |
await Task.Delay(200); | |
throw new Exception(); | |
}; | |
// Add a async lambda that does blocking operations | |
MyAsyncEvent += async (s, a) => | |
{ | |
await Task.Yield(); | |
await Task.Delay(300); | |
}; | |
// Add a async lambda that does blocking operations | |
MyAsyncEvent += async (s, a) => | |
{ | |
await Task.Yield(); | |
await Task.Delay(700); | |
}; | |
// Add an lamda to demonstrate normal exceptionn handling | |
MyEvent += (s, a) => throw new Exception(); | |
} | |
public async Task TriggerAsyncEvent() | |
{ | |
await Task.WhenAll(GetAwaitableAsyncEvents()).ConfigureAwait(false); | |
} | |
/// <summary> | |
/// Invoked from <see cref="TriggerAsyncEvent"/> to return async delegates to await | |
/// </summary> | |
/// <returns></returns> | |
private IEnumerable<Task> GetAwaitableAsyncEvents() | |
{ | |
foreach (var listener in MyAsyncEvent.GetInvocationList()) | |
if (listener.DynamicInvoke(this, new EventArgs()) is Task task) | |
yield return task; | |
} | |
public void TriggerEvent() | |
{ | |
MyEvent(this, new EventArgs()); | |
} | |
} | |
[TestFixture] | |
public class TestMyClass | |
{ | |
/// <summary> | |
/// Test that an <see cref="Exception" /> is thrown in async lambdas | |
/// </summary> | |
[Test] | |
public void Test1() | |
{ | |
Assert.That(async () => | |
{ | |
await Task.Yield(); | |
throw new Exception(); | |
}, Throws.TypeOf<Exception>()); | |
} | |
/// <summary> | |
/// Test that a synchronous event will throw an <see cref="Exception" /> | |
/// </summary> | |
[Test] | |
public void Test2() | |
{ | |
Assert.That(() => | |
{ | |
var myEvent = new MyEventClass(); | |
myEvent.TriggerEvent(); | |
}, Throws.TypeOf<Exception>()); | |
} | |
/// <summary> | |
/// Test that an non-awaited async will not catch an thrown <see cref="Exception" /> | |
/// </summary> | |
[Test] | |
public void Test3() | |
{ | |
var myEvent = new MyEventClass(); | |
//Exception gets dropped | |
var _ = myEvent.TriggerAsyncEvent(); | |
} | |
/// <summary> | |
/// Test that syncronous code will handle exceptions in an async event when awaited synchrounosly | |
/// </summary> | |
[Test] | |
public void Test4() | |
{ | |
Assert.That(() => | |
{ | |
var myEvent = new MyEventClass(); | |
myEvent.TriggerAsyncEvent().Wait(); | |
}, Throws.TypeOf<AggregateException>()); | |
} | |
/// <summary> | |
/// Finally, show that exceptions thrown in async events can be caught | |
/// </summary> | |
[Test] | |
public void Test5() | |
{ | |
Assert.That(async () => | |
{ | |
var myEvent = new MyEventClass(); | |
await myEvent.TriggerAsyncEvent(); | |
}, Throws.TypeOf<Exception>()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment