在MVVM中自动测试异步命令 [英] Automated test for a async Command in 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 IAsyncCommand
s 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屋!