【问题标题】:How to await an async Command for unit testing?如何等待异步命令进行单元测试?
【发布时间】:2019-06-03 10:30:16
【问题描述】:

我正在尝试对命令进行单元测试,但由于它是异步命令,因此测试方法会在命令完成之前进入断言。我已经查找了这个问题的解决方案,他们都在谈论创建一个我不想做的 AsyncCommand 接口等,因为我只需要等待用于单元测试目的的命令。那么是否有另一种更简单且不需要创建另一个接口等的解决方案?

这是我的命令类:

   public class Command : ICommand
    {
        public void Execute(object parameter)
        {
          //exeute...
        }

        //other stuff....
    }

那是被测试的类:

pubic class MyClass
{
    private Command commandForTest;
    public Command CommandForTest
            {
                get
                {
                    if (commandForTest == null)
                    {
                        commandForTest = new Command(async (o) =>
                        {
                            if(someCondition)
                               await SomeMethod();
                             else
                               await AnotheMrthod();   

                        });
                    }
                    return commandForTest;
                }
            }
}

这是测试方法:

[TestMethod]
        public async Task Test()
{
    MyClass myclass = new MyClass();
    await Task.Run( () =>  myclass.CommandForTest.Execute());
    //Assert....
}

【问题讨论】:

  • 我没有使用太多命令,但你可以试试这个页面:johnthiriet.com/mvvm-going-async-with-async-command/#
  • 不要测试命令,这是 UI 的东西。而是创建一个该命令调用的公共可测试方法。
  • 您不能等待从未明确表示已完成的事情。这正是 Task 存在的原因,以及异步的 void 方法无法有效测试的原因。无法编写测试并不是测试失败,而是代码失败。

标签: c# multithreading unit-testing async-await icommand


【解决方案1】:

那么有没有另一种更简单且不需要创建另一个接口等的解决方案?

不,是的。还有另一种解决方案。它更简单。最简单直接的解决方案是使用IAsyncCommand interface。或者AsyncCommand 实现,您的单元测试可以将ICommand 转换为(更脆弱)。

但如果你想走硬路线,那么是的,你可以从技术上检测async void 方法何时完成。您可以通过编写自己的SynchronizationContext and listening to OperationStarted and OperationCompleted 来做到这一点。您还需要构建一个工作队列并编写一个处理该队列的主循环。

我有一个可以做到这一点的类型。它被称为AsyncContext and it is part of AsyncEx。用法:

[TestMethod]
public void Test() // note: not async
{
  MyClass myclass = new MyClass();
  AsyncContext.Run(() =>
  {
    myclass.CommandForTest.Execute();
  });
  //Assert....
}

再次,我强烈建议使用IAsyncCommand。真正的问题是核心 MVVM 类型不足。所以大多数人使用IAsyncCommandMvxAsyncCommandAsyncCommand 或将命令逻辑公开为VM 上的async Task 方法。

【讨论】:

  • FYI 我刚刚查看了您关于 IAsyncCommand 的文章,在方法 NotifyCommandStarting 中的 图 11 中有一个小错误。如果 CTS 未取消,则不会调用 RaiseCanExecuteChanged,但必须调用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-27
  • 2017-06-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-20
  • 2017-07-15
  • 2018-03-01
相关资源
最近更新 更多