C# MVVM中异步命令的自动测试

C# MVVM中异步命令的自动测试,c#,wpf,unit-testing,mvvm,integration-testing,C#,Wpf,Unit Testing,Mvvm,Integration Testing,我有一个异步命令类,如下所示: public AsyncDelegateCommand(Func<Task> execute, Func<bool> canExecute) { this.execute = execute; this.canExecute = canExecute; } public virtual bool CanExecute(object parameter) { if(executing) return

我有一个异步命令类,如下所示:

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();
}
当我进行单元测试时,它工作得很好,因为我会立即从任务中返回

然而,在编写集成测试时,我遇到了麻烦。我不能等待执行,因为它是无效的,我不能将它更改为任务。我最后做了这个::/

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

var informationViewModel = findProductViewModel.ProductViewModel.ProductInformationViewModel;

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

这个测试有更好的解决方案吗?也许这取决于实际需要多长时间,而不是估计等待多长时间

如果要检查某个状态,您可能只需要使用:

它将比Thread.Sleepas可靠得多,因为它允许您在继续之前检查条件是否正确

e、 g


如果要检查某个状态,您可能只需要使用:

它将比Thread.Sleepas可靠得多,因为它允许您在继续之前检查条件是否正确

e、 g


你可以参考@StephenCleary的一篇好博客文章: async void通常是要避免的,因此他为异步命令引入了一个新接口及其基本实现:IAsyncCommand。此接口包含一个方法异步任务ExecuteAsyncobject参数,您可以在测试中等待该参数

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。

您可以参考@StephenCleary的一篇好博客文章: async void通常是要避免的,因此他为异步命令引入了一个新接口及其基本实现:IAsyncCommand。此接口包含一个方法异步任务ExecuteAsyncobject参数,您可以在测试中等待该参数

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

这个测试有更好的解决方案吗?也许这取决于实际需要多长时间,而不是估计等待多长时间

当然:让命令可以等待并等待它。异步void方法是一种不好的做法,只能用于事件处理程序

和中提供了一些可供使用的命令,您可以使用这些命令,或者至少可以查看这些命令以供参考

这个测试有更好的解决方案吗?也许这取决于实际需要多长时间,而不是估计等待多长时间

当然:让命令可以等待并等待它。异步void方法是一种不好的做法,只能用于事件处理程序


和中提供了一些可供参考的命令。

谢谢。看来你的链接把我指向了StephanCleary。这与@dymanoid建议的相同。因为他提供了一个例子,我会给他一个被接受的答案,但请投赞成票。谢谢。看来你的链接把我指向了StephanCleary。这与@dymanoid建议的相同。因为他提供了一个例子,我会给他一个被接受的答案,但我会投赞成票。非常有用。下次我不能等待执行时,我可能会考虑到这一点。我已经被证明,等待可以成为可能。谢谢,非常有用。下次我不能等待执行时,我可能会考虑到这一点。我已经被证明,等待可以成为可能。非常感谢。