等待单元测试的Async Void方法调用

我有一个如下所示的方法:

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转换为Async / Await。 由于我可以等待BackgroundWorker完成,所以后台工作人员正常工作。

但似乎没有办法等待异步无效的方法......

我怎样才能单元测试这种方法?


一个async void方法本质上是一个“火再忘”的方法。 没有办法找回完成事件(没有外部事件等)。

如果你需要单元测试这个,我建议把它变成一个async Task方法。 然后可以对结果调用Wait() ,该方法完成后会通知您。

但是,这种写入的测试方法仍然不起作用,因为您并不是直接测试DoStuff ,而是测试包装它的DelegateCommand 。 您需要直接测试此方法。


你应该避免async void 。 仅对事件处理程序使用async voidDelegateCommand是(逻辑上)一个事件处理程序,所以你可以这样做:

// 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


我想出了一种做单元测试的方法:

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

这里的关键是,因为我是单元测试,我可以在任务中替换我想要的异步调用(在我的异步无效中)返回。 然后我才确定任务已完成,然后再继续。

链接地址: http://www.djcxy.com/p/50797.html

上一篇: Await a Async Void method call for unit testing

下一篇: unit testing asynchronous operation