21

The following asynchronous xUnit.net test with a lambda marked with the async modifier fails by reporting that no exception was thrown:

 [Theory, AutoWebData] public async Task SearchWithNullQueryThrows( SearchService sut, CancellationToken dummyToken) { // Fixture setup // Exercise system and verify outcome Assert.Throws<ArgumentNullException>(async () => await sut.SearchAsync(null, dummyToken)); // Teardown } 

To make sure that an ArgumentNullException is actually thrown I explicitly used a try-catch block. It worked, however the resulting code is not clean (compared to the first test):

[Theory, AutoWebData] public async Task SearchWithNullQueryThrows( SearchService sut, CancellationToken dummyToken) { // Fixture setup var expected = typeof(ArgumentNullException); Type actual = null; // Exercise system try { await sut.SearchAsync(null, dummyToken); } catch (ArgumentNullException e) { actual = e.GetType(); } // Verify outcome Assert.Equal(expected, actual); // Teardown } 

Why the Assert.Throws<T> with the lambda marked with the async modifier fails?

2
  • 3
    This is a known issue: xunit.codeplex.com/workitem/9799 Commented Jan 1, 2013 at 0:50
  • +1 It seems to be fixed in snapshot 03e3be9a6781 which is before the snapshot 16883cb2351f where release 2.0.0-alpha has been associated. However it still doesn't work after updating the solution with NuGet Package Manager. Commented Jan 1, 2013 at 20:53

2 Answers 2

39

Update

This has been solved in xUnit 2, with the addition of Assert.ThrowsAsync.


I am suspecting that Assert.Throws is not async-aware. I recommend raising this issue with the xUnit team, suggesting a ThrowsAsync be added.

An async delegate in this case is returning Task or Task<T>, and the ArgumentNullException is not thrown out of the delegate directly; instead, it is placed on the Task (Task.Exception.InnerException). Assert.Throws is expecting the exception to be thrown out of the delegate directly, not placed on a property of the return value.

You can create your own AssertEx.ThrowsAsync as such:

public static async Task ThrowsAsync<TException>(Func<Task> func) { var expected = typeof(TException); Type actual = null; try { await func(); } catch (Exception e) { actual = e.GetType(); } Assert.Equal(expected, actual); } 

which can be used as such:

[Theory, AutoWebData] public async Task SearchWithNullQueryThrows( SearchService sut, CancellationToken dummyToken) { // Fixture setup // Exercise system and verify outcome await AssertEx.ThrowsAsync<ArgumentNullException>(async () => await sut.SearchAsync(null, dummyToken)); // Teardown } 

I use a similar approach in MSTest.

Sign up to request clarification or add additional context in comments.

1 Comment

I modified your code slightly, as I believe as it stood only 'ArgumentNullException' would ever get as far as the Assert. Another thing that might be worth considering is that the original Assert.Throws returns the exception (so that you can test exception message too, for example).
3

If you also need to return the exception to verify it then this might be useful:

public static async Task<Exception> AssertThrowsAsync<TException>(Func<Task> func) { var expected = typeof (TException); Exception exception = null; Type actual = null; try { await func(); } catch (Exception e) { actual = e.GetType(); exception = e; } Assert.NotNull(exception); Assert.Equal(expected, actual); return exception; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.