Skip to content

Instantly share code, notes, and snippets.

@danielrbradley
Last active January 4, 2016 02:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielrbradley/6671613 to your computer and use it in GitHub Desktop.
Save danielrbradley/6671613 to your computer and use it in GitHub Desktop.
Custom Throws assert for the NUnit framework for handling async code.
namespace NUnit.Framework
{
using System;
using System.Threading.Tasks;
using NUnit.Framework;
public static class AsyncAsserts
{
/// <summary>
/// Assert that an async method fails due to a specific exception.
/// This exception can be thrown directly or be the root cause of an aggregate exception.
/// </summary>
/// <typeparam name="T">Exception type expected</typeparam>
/// <param name="testCode">Test async delegate</param>
public static void Throws<T>(Func<Task> testCode)
where T : Exception
{
try
{
Task.WaitAll(testCode());
Assert.Fail("Expected exception of type: {0}", typeof(T));
}
catch (AggregateException aex)
{
if (!aex.BaseIsOfType<T>())
{
Assert.Fail(
"Expected aggregate exception with base type: {0}"
+ "\r\nBut got an aggregate exception with base type: {1}",
typeof(T),
aex.GetBaseException().GetType());
}
// Continue excecution if base exception was expected.
}
catch (T)
{
// Swallow exception as this is correct.
}
}
}
public static class AggregateExceptionExtensions
{
public static bool BaseIsOfType<T>(this AggregateException aex)
{
return aex.GetBaseException() is T;
}
}
}
namespace Tests
{
using NUnit.Framework;
// Example AsyncAsserts.Throws<T>(testCode) usage.
[Category("Integration")]
public class CreateTest
{
[Test]
public void ArgumentException()
{
AsyncAsserts.Throws<ArgumentException>(
() => Service.Create(""));
}
}
}
@Funbit
Copy link

Funbit commented Apr 1, 2015

I would do it this way:

public static class AsyncAssert
{
    /// <summary>
    /// Assert that an async method fails due to a specific exception.
    /// </summary>
    /// <typeparam name="T">Exception type expected</typeparam>
    /// <param name="asyncDelegate">Test async delegate</param>
    public static async Task Throws<T>(Func<Task> asyncDelegate) where T : Exception
    {
        try
        {
            await asyncDelegate();
            Assert.Fail("Expected exception of type: {0}", typeof (T));
        }
        catch (T)
        {
            // swallow this exception because it is expected
        }
        catch (AssertionException)
        {
            throw;
        }
        catch (Exception ex)
        {
            Assert.Fail("Expected exception of type: {0} but was: {1}", typeof(T), ex);
        }
    }
}

And usage is:

[Test]
public async void AsyncErrorTest()
{
    await AsyncAssert.Throws<ArgumentException>(async () =>
    {
        await MyStuffAsync();
    });
}

This way there is no need to play with aggregated exceptions so the result is exactly the same as in the production code (where you probably use await).

@davidpeden3
Copy link

Even better, return the exception so that properties like .Message can be inspected:

        public static async Task<T> ThrowsAsync<T>(Func<Task> asyncDelegate) where T : Exception
        {
            T caughtException = null;

            try
            {
                await asyncDelegate();
                Assert.Fail("Expected exception of type: {0}", typeof(T));
            }
            catch (T expectedException)
            {
                caughtException = expectedException;
            }
            catch (AssertionException)
            {
                throw;
            }
            catch (Exception ex)
            {
                Assert.Fail("Expected exception of type: {0} but was: {1}", typeof(T), ex);
            }

            return caughtException;
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment