如何在MVVM中对调用异步方法的DelegateCommand进行单元测试

16

我刚接触单元测试的MVVM和使用PRISM在我的项目中。我正在实现当前项目的单元测试,但无法找到在线资源告诉我如何测试调用异步方法的DelegateCommand。这是对我发布的文章“如何测试带有异步方法的ViewModel”的追问,想要测试MVVM中的异步方法,回答是可以使用async TestMethod测试公共方法。但只适用于我想要测试的方法是公共方法时才有效。

问题是我想测试我的DelegateCommand,因为这些是我想要在其他类中公开的唯一公共细节,而其他所有细节都是私有的。我可以将私有方法公开为公共方法,但我永远不会这样做,因为这是一个糟糕的设计。我不确定该如何解决这个问题 - 是否需要测试DelegateCommand,还是有其他方法?我想知道其他人是如何处理这种情况的,并引导我走向正确的方向。

以下是我的代码:

 async void GetTasksAsync()
        {
            this.SimpleTasks.Clear();
            Func<IList<ISimpleTask>> taskAction = () =>
                {
                    var result = this.dataService.GetTasks();
                    if (token.IsCancellationRequested)
                        return null;
                    return result;
                };
            IsBusyTreeView = true;

            Task<IList<ISimpleTask>> getTasksTask = Task<IList<ISimpleTask>>.Factory.StartNew(taskAction, token);
            var l = await getTasksTask;          // waits for getTasksTask


            if (l != null)
            {
                foreach (ISimpleTask t in l)
                {
                    this.SimpleTasks.Add(t); // adds to ViewModel.SimpleTask
                }
            }
        }

以下是在我的虚拟机中调用上述异步方法的命令。

  this.GetTasksCommand = new DelegateCommand(this.GetTasks);
      void GetTasks()
        {
                GetTasksAsync();
        }

现在我的测试方法如下:

 [TestMethod]
        public void Command_Test_GetTasksCommand()
        {
          MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask 
          Assert.IsTrue(MyBiewModel.SimpleTask != null)
        } 

目前我遇到的问题是,我的ViewModel.SimpleTask = null,这是因为它没有等待异步方法完成。

3个回答

20

我编写了一个 AsyncCommand 类,它从 Execute 方法返回 Task 对象。然后,您需要显式实现ICommand.Execute方法,并等待来自您的Execute实现的 Task:

public class AsyncCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    public Func<Task> ExecutedHandler { get; private set; }

    public Func<bool> CanExecuteHandler { get; private set; }

    public AsyncCommand(Func<Task> executedHandler, Func<bool> canExecuteHandler = null)
    {
        if (executedHandler == null)
        {
            throw new ArgumentNullException("executedHandler");
        }

        this.ExecutedHandler = executedHandler;
        this.CanExecuteHandler = canExecuteHandler;
    }

    public Task Execute()
    {
        return this.ExecutedHandler();
    }

    public bool CanExecute()
    {
        return this.CanExecuteHandler == null || this.CanExecuteHandler();
    }

    public void RaiseCanExecuteChanged()
    {
        if (this.CanExecuteChanged != null)
        {
            this.CanExecuteChanged(this, new EventArgs());
        }
    }

    bool ICommand.CanExecute(object parameter)
    {
        return this.CanExecute();
    }

    async void ICommand.Execute(object parameter)
    {
        await this.Execute();
    }
}

然后,您可以将异步的返回Task的方法传递给命令类:

public class ViewModel
{
    public AsyncCommand AsyncCommand { get; private set; }

    public bool Executed { get; private set; }

    public ViewModel()
    {
        Executed = false;
        AsyncCommand = new AsyncCommand(Execute);
    }

    private async Task Execute()
    {
        await(Task.Delay(1000));
        Executed = true;
    }
}

在你的单元测试中,你只需等待Execute方法:

[TestMethod]
public async Task TestAsyncCommand()
{
    var viewModel = new ViewModel();

    Assert.IsFalse(viewModel.Executed);
    await viewModel.AsyncCommand.Execute();

    Assert.IsTrue(viewModel.Executed);
}

另一方面,UI将调用显式实现的ICommand.Execute方法,该方法负责等待任务。

(*) 与此同时,我注意到如果遵循常见的命名惯例,返回任务的方法应该实际上被命名为ExecuteAsync


0

由于我无法添加评论,为了完整起见,在 PRISM 6 中,您可以尝试:

ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));

这并没有回答问题。问题是如何测试执行async处理程序的DelegateCommand,而不是如何创建这样的命令。 - zmechanic

0
在 Prism 6 中,您可以从异步处理程序创建 DelegateCommandDelegateCommand<T>
例如: startParsingCommand=DelegateCommand .FromAsyncHandler(StartParsingAsync,CanStartParsing) .ObservesProperty(()=> IsParserStarted);

1
FromAsyncHandler 在 Prism 6.x 中已经被弃用(我不知道是哪个 x :D),那么有更好的方法来做这件事吗? - sorosh_sabz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接