自动化测试在MVVM一个异步命令(Automated test for a async Comman

2019-10-28 14:47发布

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

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
}

当我的单元测试,它工作得很好,因为我立即从任务返回。

然而,写我的集成测试的时候,我遇到了麻烦。 我不能等待Execute ,因为它是void的,我无法将其更改为Task 。 我最终会做这样的::/

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

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;

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

是否有此测试更好的解决办法? 也许东西是它实际上需要多长时间依赖,并没有估计要等多长时间。

Answer 1:

:您可以通过@StephenCleary指好的博客文章https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
async void通常被避免,所以他引入了一个异步命令一个新的接口(和它的碱实施): IAsyncCommand 。 该接口包含一个方法async Task ExecuteAsync(object parameter) ,你可以在你的测试等待。

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

这样的异步命令的简单的实现应该是这样的:

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



Answer 2:

是否有此测试更好的解决办法? 也许东西是它实际上需要多长时间依赖,并没有估计要等多长时间。

当然:使命令awaitable和await它。 async void的方法是不好的做法,并意味着用于事件处理程序。

有可用awaitable命令Mvvm.Async和ReactiveUI ,您可以使用,或者至少看看,以供参考。



Answer 3:

如果你有一个状态来检查,你应该只使用SpinWait.SpinUntil :

它会比更可靠Thread.Sleep ,因为它可以让你检查,如果条件是继续之前如此。

findProductViewModel.FindProductCommand.Execute(null);
SpinWait.SpinUntil(() => findProductViewModel.ProductViewModel.ProductInformationViewModel != null, 5000);

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;

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


文章来源: Automated test for a async Command in MVVM