Created
August 18, 2017 00:54
-
-
Save NZSmartie/2b66924cffc98047f995c7f3813046eb to your computer and use it in GitHub Desktop.
Handle Exceptions from async EventHandler with Nunit tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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