在MVVM中自动测试异步命令 [英] Automated test for a async Command in MVVM

查看:203
本文介绍了在MVVM中自动测试异步命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个异步的Command类,如下所示:

I have an asynchronous Command class, like so:

public AsyncDelegateCommand(Func<Task> execute, Func<bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

public virtual bool CanExecute(object parameter)
{
    if(executing)
        return false;
    if(canExecute == null)
        return true;
    return canExecute();
}

public async void Execute(object parameter) // Notice "async void"
{
    executing = true;
    CommandManager.InvalidateRequerySuggested();
    if(parameter != null && executeWithParameter != null)
        await executeWithParameter(parameter);
    else if(execute != null)
        await execute();
    executing = false;
    CommandManager.InvalidateRequerySuggested();
}

它的名称如下:

FindProductCommand = new AsyncDelegateCommand(TryToFindProduct, () => CanFindProduct() && connector.HasConnection);

private async Task TryToFindProduct()
{
    //code
}

当我进行单元测试时,它可以正常工作,因为我会立即从任务中返回.

When I unit tests, it works just fine, since I'm returning instantly from tasks.

但是,在编写集成测试时,我遇到了麻烦.我无法等待Execute,因为它是void,并且无法将其更改为Task.我最终这样做::/

However, when writing my integration test, I run into trouble. I am not able to await Execute, since it is void, and I am not able to change it to Task. I end up doing this: :/

findProductViewModel.FindProductCommand.Execute(null);
Thread.Sleep(2000);

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;

Assert.AreEqual("AFG00", informationViewModel.ProductGroup);

此测试是否有更好的解决方案?也许有些事情取决于它实际需要多长时间,而没有估计要等待多长时间.

Is there a better solution for this test? Maybe something that is reliant on how long it actually takes, and doesn't estimate how long to wait.

推荐答案

您可以通过@StephenCleary引用一篇不错的博客文章:

You can refer to a good blog post by @StephenCleary: https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
async void is generally to be avoided, so he introduces a new interface (and its base implementation) for an async command: IAsyncCommand. This interface contains a method async Task ExecuteAsync(object parameter) that you could await in your tests.

public interface IAsyncCommand : ICommand
{
  Task ExecuteAsync(object parameter);
}

public abstract class AsyncCommandBase : IAsyncCommand
{
  public abstract bool CanExecute(object parameter);
  public abstract Task ExecuteAsync(object parameter);

  public async void Execute(object parameter)
  {
    await ExecuteAsync(parameter);
  }

  public event EventHandler CanExecuteChanged
  {
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
  }

  protected void RaiseCanExecuteChanged()
  {
    CommandManager.InvalidateRequerySuggested();
  }
}

这种异步命令的最简单实现如下所示:

The simplest implementation of such an async command would look like this:

public class AsyncCommand : AsyncCommandBase
{
  private readonly Func<Task> _command;

  public AsyncCommand(Func<Task> command)
  {
    _command = command;
  }

  public override bool CanExecute(object parameter)
  {
    return true;
  }

  public override Task ExecuteAsync(object parameter)
  {
    return _command();
  }
}

但是您可以在链接的博客文章中找到更高级的变体.您可以在代码中完全使用IAsyncCommand,因此可以对其进行测试.您使用的MVVM框架也会很高兴,因为该接口基于ICommand.

But there are more advanced variants that you can find in the linked blog post. You can use the IAsyncCommands in your code all the way, so you can test them. The MVVM framework you use will also be happy, because that interface is based on the ICommand.

这篇关于在MVVM中自动测试异步命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆