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