我有一个异步的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);
是否有此测试更好的解决办法? 也许东西是它实际上需要多长时间依赖,并没有估计要等多长时间。
:您可以通过@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
。
是否有此测试更好的解决办法? 也许东西是它实际上需要多长时间依赖,并没有估计要等多长时间。
当然:使命令awaitable和await
它。 async
void
的方法是不好的做法,并只意味着用于事件处理程序。
有可用awaitable命令Mvvm.Async和ReactiveUI ,您可以使用,或者至少看看,以供参考。
如果你有一个状态来检查,你应该只使用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);