У меня есть метод, который похож на это:
private async void DoStuff(long idToLookUp)
{
IOrder order = await orderService.LookUpIdAsync(idToLookUp);
// Close the search
IsSearchShowing = false;
}
//Other stuff in case you want to see it
public DelegateCommand<long> DoLookupCommand{ get; set; }
ViewModel()
{
DoLookupCommand= new DelegateCommand<long>(DoStuff);
}
Я пробую к модульному тесту его как это:
[TestMethod]
public void TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
myViewModel.DoLookupCommand.Execute(0);
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Мой утверждаем называют, прежде чем я буду сделан с копируемым LookUpIdAsync. В моем нормальном коде, который является, что я хочу. Но для моего Модульного теста я не хочу это.
Я преобразовываю в Асинхронный/ждем от использования BackgroundWorker. Со второстепенным рабочим это функционировало правильно, потому что я мог ожидать BackgroundWorker для окончания.
Но, кажется, нет способа ожидать асинхронного пустого метода...
Как может я модульный тест этот метод?
Я выяснил способ сделать это для поблочного тестирования:
[TestMethod]
public void TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
var lookupTask = Task<IOrder>.Factory.StartNew(() =>
{
return new Order();
});
orderService.LookUpIdAsync(Arg.Any<long>()).Returns(lookupTask);
//+ Act
myViewModel.DoLookupCommand.Execute(0);
lookupTask.Wait();
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
ключ здесь то, что, потому что я - поблочное тестирование, которым я могу заменить в задаче, которую я хочу иметь свой асинхронный вызов (в моей асинхронной пустоте) для возврата. Я затем просто удостоверяюсь, что задача завершилась, прежде чем я буду идти дальше.
Единственным путем я знаю, должен повернуть Ваш async void
метод к async Task
метод
Измените свой метод для возврата Задачи, и можно использовать Задачу. Результат
bool res = configuration.InitializeAsync(appConfig).Result;
Assert.IsTrue(res);
Предоставленный ответ тестирует команду а не асинхронный метод. Как упомянуто выше Вам будет нужен другой тест для тестирования того асинхронного метода также.
После проведения некоторого времени с подобной проблемой я нашел, что легкое ожидание протестировало асинхронный метод в модульном тесте, просто призвав синхронно:
protected static void CallSync(Action target)
{
var task = new Task(target);
task.RunSynchronously();
}
и использование:
CallSync(() => myClass.MyAsyncMethod());
тест ожидает на этой строке и продолжается после того, как результат готов, таким образом, мы можем утверждать сразу впоследствии.
Можно использовать AutoResetEvent для остановки метода тестирования, пока асинхронный вызов не завершается:
[TestMethod()]
public void Async_Test()
{
TypeToTest target = new TypeToTest();
AutoResetEvent AsyncCallComplete = new AutoResetEvent(false);
SuccessResponse SuccessResult = null;
Exception FailureResult = null;
target.AsyncMethodToTest(
(SuccessResponse response) =>
{
SuccessResult = response;
AsyncCallComplete.Set();
},
(Exception ex) =>
{
FailureResult = ex;
AsyncCallComplete.Set();
}
);
// Wait until either async results signal completion.
AsyncCallComplete.WaitOne();
Assert.AreEqual(null, FailureResult);
}
Необходимо избежать async void
. Только используйте async void
для обработчиков событий. DelegateCommand
(логически) обработчик событий, таким образом, можно сделать это как это:
// Use [InternalsVisibleTo] to share internal methods with the unit test project.
internal async Task DoLookupCommandImpl(long idToLookUp)
{
IOrder order = await orderService.LookUpIdAsync(idToLookUp);
// Close the search
IsSearchShowing = false;
}
private async void DoStuff(long idToLookUp)
{
await DoLookupCommandImpl(idToLookup);
}
и модульный тест это как:
[TestMethod]
public async Task TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
await myViewModel.DoLookupCommandImpl(0);
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
<час> Мой рекомендуемый ответ выше. Но если Вы действительно хотите протестировать async void
метод, можно сделать так с моим библиотека AsyncEx :
[TestMethod]
public void TestDoStuff()
{
AsyncContext.Run(() =>
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
myViewModel.DoLookupCommand.Execute(0);
});
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
, Но это решение изменяется SynchronizationContext
для Вашей модели представления в течение ее времени жизни.
async void
метод является по существу "огнем, и забудьте" метод. Нет никакого средства возвращения события завершения (без внешнего события, и т.д.).
, Если бы Вам нужно к модульному тесту это, я рекомендовал бы делать его async Task
метод вместо этого. Можно затем звонить Wait()
на результатах, которые уведомят Вас, когда метод завершится.
Однако этот метод тестирования, как записано все еще не работал бы, поскольку Вы на самом деле не тестируете DoStuff
непосредственно, а скорее тестируете DelegateCommand
, который переносит его. Необходимо было бы протестировать этот метод непосредственно.