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?
Assert. Throws<Exception>(() => SomethingThatThrowsAnException()); If the method SomethingThatThrowsAnException() from the above throws an exception the assertion passes, if it does not throw an exception, the assertion will fail and thereby the test fails. It is as simple as that.
Assert. Throws returns the exception that's thrown which lets you assert on the exception.
If you need to test for exceptions, there are less frowned upon ways. You can use the try{act/fail}catch{assert} method, which can be useful for frameworks that don't have direct support for exception tests other than ExpectedException .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With