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

XUnit и Moq не поддерживают ключевые слова async-ожидания

Я пытаюсь выяснить, как применять async и ждать ключевых слов для моих тестов xUnit. Я использую xUnit 1.9 и Async CTP 1.3. Вот мой тестовый пример

У меня есть интерфейс, который определяет один асинхронный вызов метода

public interface IDoStuffAsync
{
    Task AnAsyncMethod(string value);
}

У меня есть класс, который использует интерфейс и вызывает метод async

public class UseAnAsyncThing
{
    private readonly IDoStuffAsync _doStuffAsync;

    public UseAnAsyncThing(IDoStuffAsync doStuffAsync)
    {
        _doStuffAsync = doStuffAsync;
    }

    public async Task DoThatAsyncOperation(string theValue)
    {
        await _doStuffAsync.AnAsyncMethod(theValue);
    }
}

В моих тестах я хочу проверить, что метод DoThatAsyncOperation вызывает метод с правильным значением, поэтому я макетирую интерфейс и использую Moq для проверки вызова

    [Fact]
    public async void The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        await sut.DoThatAsyncOperation("test");

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

В этом тесте используются ключевые слова async и await. Когда он запускается, он ошибочно проходит, поскольку Moq должен утверждать, что проверка не выполняется. Любой код после вызова sut.DoThatAsyncOperation("test"); не запускается

    [Fact]
    public void This_will_work_and_assert_the_reslt()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        sut.DoThatAsyncOperation("test").ContinueWith(y => { });

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

Этот тест настраивается без ожидающих и асинхронных ключевых слов и проходит нормально.

Является ли это ожидаемым поведением для xUnit и Moq?


Обновление

Спасибо за комментарий Стивена. Мне удалось исправить первый тест, сделав два изменения. Тест теперь возвращает задачу вместо void, а Mock также возвращает задачу.

    [Fact]
    public async Task The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())).ReturnAsync(true);

        await sut.DoThatAsyncOperation("test");

        // This now fails as it should
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }
4b9b3361

Ответ 1

Измените метод unit test, чтобы вернуть Task вместо void, и он должен работать. Поддержка async void модульных тестов рассматривается для будущей версии.

Я подробно описываю почему асинхронные модульные тесты не работают по умолчанию в моем блоге. (В моих примерах в блоге используется MSTest, но те же проблемы существовали и в каждом другом тестировщике, включая xUnit pre-1.9).

Ответ 2

Я попытался использовать код из вашего "Обновить", но он остановился на асинхронном методе, который я издевался.

    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(default(T));


    mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(tcs.Task);

Итак, чтобы исправить это, мне пришлось изменить метод "Return":

    mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(()=> { return tcs.Task; } );