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?
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 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;
}