Тестирование исключений в асинхронных методах

Я немного застрял с этим кодом (это образец):

public async Task Fail()
{
    await Task.Run(() => { throw new Exception(); });
}

[Test]
public async Task TestFail()
{
    Action a = async () => { await Fail(); };
    a.ShouldThrow<Exception>();
}

Код не вызывает исключения и

Ожидалось исключение System.Exception, но никаких исключений не было.

Я уверен, что я что-то упустил, но, похоже, документы показывают, что это путь. Некоторая помощь будет оценена по достоинству.

Ответ 1

Вы должны использовать Func<Task> вместо Action:

[Test]
public void TestFail()
{
    Func<Task> f = async () => { await Fail(); };
    f.ShouldThrow<Exception>();            
}

Это вызовет следующее расширение, которое используется для проверки асинхронных методов

public static ExceptionAssertions<TException> ShouldThrow<TException>(
    this Func<Task> asyncAction, string because = "", params object[] becauseArgs)
        where TException : Exception        

Внутренне этот метод запускает задачу, возвращаемую Func и ждет ее. Что-то вроде

try
{
    Task.Run(asyncAction).Wait();
}
catch (Exception exception)
{
    // get actual exception if it wrapped in AggregateException
}

Обратите внимание, что сам тест является синхронным.

Ответ 2

С текущими утверждениями v5+ код будет выглядеть так:

ISubject sut = BuildSut();
//Act and Assert
Func<Task> sutMethod = async () => { await sut.SutMethod("whatEverArgument"); };
await sutMethod.Should().ThrowAsync<Exception>();

Это должно работать.