70

I am trying to port a test to NUnit3 and am getting a System.ArgumentException : 'async void' methods are not supported, please use 'async Task' instead.

[Test]
public void InvalidUsername()
{
    ...
    var exception = Assert.Throws<HttpResponseException>(async () => await client.LoginAsync("notarealuser@example.com", testpassword));
    exception.HttpResponseMessage.StatusCode.ShouldEqual(HttpStatusCode.BadRequest); // according to http://tools.ietf.org/html/rfc6749#section-5.2
    ...
}

Assert.Throws appears to take a TestDelegate, defined as:

public delegate void TestDelegate();

hence the ArgumentException. What is the best way to port this code?

Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
tikinoa
  • 1,267
  • 2
  • 10
  • 14

5 Answers5

118

This was resolved by Nunit. You can now use Assert.ThrowsAsync<>()

https://github.com/nunit/nunit/issues/1190

Example:

Assert.ThrowsAsync<Exception>(() => YourAsyncMethod());
Zabbu
  • 1,387
  • 1
  • 8
  • 11
13

I would recommend the following code instead of Assert.ThrowsAsync, as this is more readable:

// Option A
[Test]
public void YourAsyncMethod_Throws_YourException_A()
{
    // Act
    AsyncTestDelegate act = () => YourAsyncMethod();

    // Assert
    Assert.That(act, Throws.TypeOf<YourException>());
}

// Option B (local function)
[Test]
public void YourAsyncMethod_Throws_YourException_B()
{
    // Act
    Task Act() => YourAsyncMethod();

    // Assert
    Assert.That(Act, Throws.TypeOf<YourException>());
}
arni
  • 2,152
  • 19
  • 14
1

I ended up writing a static function that mirrors what NUnit does. There was a whole conversation at https://github.com/nunit/nunit/issues/464 about this.

public static async Task<T> Throws<T>(Func<Task> code) where T : Exception
{
    var actual = default(T);

    try
    {
        await code();
        Assert.Fail($"Expected exception of type: {typeof (T)}");
    }
    catch (T rex)
    {
        actual = rex;
    }
    catch (Exception ex)
    {
        Assert.Fail($"Expected exception of type: {typeof(T)} but was {ex.GetType()} instead");
    }

    return actual;
}

Then from my tests I can use it such as

var ex = await CustomAsserts.Throws<HttpResponseException>(async () => await client.DoThings());
Assert.IsTrue(ex.Response.StatusCode == HttpStatusCode.BadRequest);
Eric
  • 231
  • 3
  • 9
0

To ensure the exception was thrown, it's better to not assert in the catch block if you so choose to use one. This way, you can be sure the correct exception type is thrown because otherwise you'll get a null reference or an uncaught different exception.

HttpResponseException expectedException = null;
try
{
    await  client.LoginAsync("notarealuser@example.com", testpassword));         
}
catch (HttpResponseException ex)
{
    expectedException = ex;
}

Assert.AreEqual(HttpStatusCode.NoContent, expectedException.Response.BadRequest);
BrianPMorin
  • 67
  • 1
  • 5
  • Hi @BrianPMorin, throwing `NullReferenceException` should be avoided as it would lead to a confusion while analysing a failed test. I'd suggest you to write `Assert.That(expectedException, Is.NotNull)` before checking the HTTP Status Code. – AlexMelw Oct 20 '22 at 11:54
-5

You could try using something like this:

 try
 {
     await client.LoginAsync("notarealuser@example.com", testpassword);
 }
 catch (Exception ex)
 {
     Assert.That(ex, Is.InstanceOf(typeof (HttpResponseException)));
 }
angelcool.net
  • 2,505
  • 1
  • 24
  • 26
  • 1
    Hi @woitheq, `await client.LoginAsync("notarealuser@example.com", testpassword);` could not throw an exception. In that case the `catch` block and as a result the `Assert` statement are ignored. – AlexMelw Oct 20 '22 at 11:21