Ждите Асинхронного Пустого вызова метода поблочного тестирования

У меня есть метод, который похож на это:

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 для окончания.

Но, кажется, нет способа ожидать асинхронного пустого метода...

Как может я модульный тест этот метод?

62
задан 8 January 2013 в 02:57

7 ответов

Я выяснил способ сделать это для поблочного тестирования:

[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();
}

ключ здесь то, что, потому что я - поблочное тестирование, которым я могу заменить в задаче, которую я хочу иметь свой асинхронный вызов (в моей асинхронной пустоте) для возврата. Я затем просто удостоверяюсь, что задача завершилась, прежде чем я буду идти дальше.

17
ответ дан 31 October 2019 в 13:39

Единственным путем я знаю, должен повернуть Ваш async void метод к async Task метод

5
ответ дан 31 October 2019 в 13:39

Измените свой метод для возврата Задачи, и можно использовать Задачу. Результат

bool res = configuration.InitializeAsync(appConfig).Result;
Assert.IsTrue(res);
1
ответ дан 31 October 2019 в 13:39

Предоставленный ответ тестирует команду а не асинхронный метод. Как упомянуто выше Вам будет нужен другой тест для тестирования того асинхронного метода также.

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

    protected static void CallSync(Action target)
    {
        var task = new Task(target);
        task.RunSynchronously();
    }

и использование:

CallSync(() => myClass.MyAsyncMethod());

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

4
ответ дан 31 October 2019 в 13:39

Можно использовать 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);
}
5
ответ дан 31 October 2019 в 13:39

Необходимо избежать 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 для Вашей модели представления в течение ее времени жизни.

56
ответ дан 31 October 2019 в 13:39

async void метод является по существу "огнем, и забудьте" метод. Нет никакого средства возвращения события завершения (без внешнего события, и т.д.).

, Если бы Вам нужно к модульному тесту это, я рекомендовал бы делать его async Task метод вместо этого. Можно затем звонить Wait() на результатах, которые уведомят Вас, когда метод завершится.

Однако этот метод тестирования, как записано все еще не работал бы, поскольку Вы на самом деле не тестируете DoStuff непосредственно, а скорее тестируете DelegateCommand, который переносит его. Необходимо было бы протестировать этот метод непосредственно.

50
ответ дан 31 October 2019 в 13:39

Другие вопросы по тегам:

Похожие вопросы: