Подтвердить что ты не робот

Как обрабатывать исключения, заданные задачами в 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, не работает?

4b9b3361

Ответ 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;
    }