如何在MVVM中单元测试调用异步方法的DelegateCommand
本文关键字:调用 异步方法 DelegateCommand 单元测试 MVVM | 更新日期: 2023-09-27 18:06:35
我是单元测试MVVM和在我的项目上使用PRISM的新手。我正在我们当前的项目上实现单元测试,并没有幸运地在网上找到资源,告诉我如何测试调用异步方法的DelegateCommand。这是我的帖子的后续问题-如何用异步方法对ViewModel进行单元测试。关于如何在MVVM中对异步方法进行单元测试,并回答说公共方法可以使用async TestMethod进行测试。只有当我要测试的方法是公共方法时,这个场景才会起作用。
问题是我想测试我的DelegateCommand,因为这是我想在其他类上公开的唯一公共细节,其他一切都是私有的。我可以将我的私有方法公开,但我永远不会这样做,因为这是一个糟糕的设计。我不确定如何做到这一点-是需要测试DelegateCommand,还是有一些其他的工作围绕这一点?我很想知道其他人是怎么做的,并以某种方式引导我走上正确的道路。
下面是我的代码
async void GetTasksAsync()
{
this.SimpleTasks.Clear();
Func<IList<ISimpleTask>> taskAction = () =>
{
var result = this.dataService.GetTasks();
if (token.IsCancellationRequested)
return null;
return result;
};
IsBusyTreeView = true;
Task<IList<ISimpleTask>> getTasksTask = Task<IList<ISimpleTask>>.Factory.StartNew(taskAction, token);
var l = await getTasksTask; // waits for getTasksTask
if (l != null)
{
foreach (ISimpleTask t in l)
{
this.SimpleTasks.Add(t); // adds to ViewModel.SimpleTask
}
}
}
也是在我的VM中调用上面的async方法的命令
this.GetTasksCommand = new DelegateCommand(this.GetTasks);
void GetTasks()
{
GetTasksAsync();
}
现在我的Test Method变成了
[TestMethod]
public void Command_Test_GetTasksCommand()
{
MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask
Assert.IsTrue(MyBiewModel.SimpleTask != null)
}
目前我得到的是我的ViewModel。SimpleTask = null这是因为它不等待async方法完成
我写了一个AsyncCommand类,它从Execute
方法返回一个Task对象。然后,您需要显式地实现ICommand.Execute
,等待Execute
实现的任务:
public class AsyncCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public Func<Task> ExecutedHandler { get; private set; }
public Func<bool> CanExecuteHandler { get; private set; }
public AsyncCommand(Func<Task> executedHandler, Func<bool> canExecuteHandler = null)
{
if (executedHandler == null)
{
throw new ArgumentNullException("executedHandler");
}
this.ExecutedHandler = executedHandler;
this.CanExecuteHandler = canExecuteHandler;
}
public Task Execute()
{
return this.ExecutedHandler();
}
public bool CanExecute()
{
return this.CanExecuteHandler == null || this.CanExecuteHandler();
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, new EventArgs());
}
}
bool ICommand.CanExecute(object parameter)
{
return this.CanExecute();
}
async void ICommand.Execute(object parameter)
{
await this.Execute();
}
}
你可以将异步任务返回方法传递给命令类:
public class ViewModel
{
public AsyncCommand AsyncCommand { get; private set; }
public bool Executed { get; private set; }
public ViewModel()
{
Executed = false;
AsyncCommand = new AsyncCommand(Execute);
}
private async Task Execute()
{
await(Task.Delay(1000));
Executed = true;
}
}
在单元测试中,您只需等待Execute
方法:
[TestMethod]
public async Task TestAsyncCommand()
{
var viewModel = new ViewModel();
Assert.IsFalse(viewModel.Executed);
await viewModel.AsyncCommand.Execute();
Assert.IsTrue(viewModel.Executed);
}
另一方面,UI将调用显式实现的ICommand.Execute
方法,该方法负责等待任务。
(*)同时,我注意到,如果您遵循常见的命名约定,任务返回方法实际上应该命名为ExecuteAsync
。
在Prism 6中,您可以从异步处理程序创建DelegateCommand
和DelegateCommand<T>
。
startParsingCommand=DelegateCommand
.FromAsyncHandler(StartParsingAsync,CanStartParsing)
.ObservesProperty(()=> IsParserStarted);
由于我不能添加注释,为了完整起见,您可以在PRISM 6中尝试:
ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));