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