Как обрабатывать исключения, заданные задачами в xUnit.net Assert.Throws <T>?
Следующий асинхронный тег xUnit.net
с lambda
, отмеченный модификатором async
, завершился неудачей, сообщив, что исключение не было выбрано:
[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
}
Чтобы убедиться, что ArgumentNullException
действительно выбрано, я явно использовал блок try-catch
. Он работал, однако полученный код не является чистым (по сравнению с первым тестом):
[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
}
Почему Assert.Throws<T>
с lambda
, отмеченным с помощью модификатора async
, не работает?
Ответы
Ответ 1
Обновление
Это было решено в xUnit 2, с добавлением Assert.ThrowsAsync
.
Я подозреваю, что Assert.Throws
не async
-aware. Я рекомендую поднимать эту проблему командой xUnit, предлагая добавить ThrowsAsync
.
В этом случае делегат async
возвращает Task
или Task<T>
, а ArgumentNullException
не выбрасывается из делегата напрямую; вместо этого он помещается на Task
(Task.Exception.InnerException
). Assert.Throws
ожидает, что исключение будет выбрано из делегата напрямую, а не помещено в свойство возвращаемого значения.
Вы можете создать свой собственный AssertEx.ThrowsAsync
как таковой:
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);
}
который может быть использован как таковой:
[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
}
Я использую аналогичный подход в MSTest.
Ответ 2
Если вам также нужно вернуть исключение, чтобы проверить его, это может быть полезно:
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;
}