сбой исключений xUnit

Я не знаю, почему мои модульные тесты терпят неудачу:
Метод под испытанием:

public async Task<MyType> Get(string externalId)
{
    if (externalId == null)
        throw new ArgumentNullException();
    if (externalId == "" || externalId == " ")
        throw new FormatException();
        // ...
}

Модульный тест:

Action action = async () => await controller.Get(null);

// Act & Assert
action.ShouldThrow<ArgumentNullException>();

И есть сбой, потому что сообщение об ошибке говорит:

Ожидалось исключение ArgumentNullException, но не было выполнено.

И это странно, потому что при отладке этого теста была выброшена строка исключения!

Я использую xunit и fluentassertions.

1 ответ

  1. Эта линия проблема:

    Action action = async () => await controller.Get(null);
    

    Это создает async voidметод, который делает его чрезвычайно трудным для наблюдения исключений (и, таким образом, сделать правильное тестирование).

    Последний раз, когда я проверял, Fluent Assertions поддерживает только анти-шаблон стиля «асинхронных» утверждений:

    sut.Awaiting(…).ShouldThrow<T>();
    

    К сожалению, это использует блокировку sync-over-async hack, так что он обречен на неудачу при использовании с однопоточным SynchronizationContext. О, и угадайте, какая платформа модульного тестирования обеспечивает однопоточный SynchronizationContext? Если вы угадали xUnit, то вы получите приз.

    Таким образом,» асинхронные » тесты FA не будут работать с xUnit из коробки. У вас есть несколько вариантов.

    Можно избежать контекста xUnit, выполнив весь тест в потоке пула потоков; это неэффективно, но позволит выполнить работу:

    await Task.Run(() =>
    {
      controller.Awaiting(c => c.Get(null)).ShouldThrow<ArgumentNullException>();
    });
    

    Не очень симпатичная.

    Кроме того, можно использовать собственные асинхронно совместимые утверждения, такие как написанное мной:

    await AsyncAssert.ThrowsAsync<ArgumentNullException>(() => controller.Get(null));