Skip to content

Instantly share code, notes, and snippets.

@NZSmartie
Created August 18, 2017 00:54
Show Gist options
  • Save NZSmartie/2b66924cffc98047f995c7f3813046eb to your computer and use it in GitHub Desktop.
Save NZSmartie/2b66924cffc98047f995c7f3813046eb to your computer and use it in GitHub Desktop.
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