如何unit testing在MVVM中调用异步方法的DelegateCommand

我是unit testingMVVM的新手,在我的项目中使用PRISM。 我正在我们当前的项目上实现unit testing,而没有运气在线查找资源,这将告诉我如何调用异步方法的totest DelegateCommand。 这是我的post的后续问题 – 如何使用异步方法对ViewModel进行unit testing。 关于如何在MVVM中unit testing异步方法,并回答公共方法可以使用异步TestMethod进行测试。 仅当我要测试的方法是公共方法时,此方案才有效。

问题是我想测试我的DelegateCommand,因为这是我想要在其他类上公开的唯一公共细节,其他所有内容都是私有的。 我可以公开我的私人方法,但我永远不会这样做,因为它是一个糟糕的设计。 我不确定如何解决这个问题 – 是否需要对DelegateCommand进行测试,还是有其他一些工作? 我很想知道其他人如何去做,并以某种方式引导我走向正确的道路。

这是我的代码

async void GetTasksAsync() { this.SimpleTasks.Clear(); Func<IList> taskAction = () => { var result = this.dataService.GetTasks(); if (token.IsCancellationRequested) return null; return result; }; IsBusyTreeView = true; Task<IList> getTasksTask = Task<IList>.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中的命令,它调用上面的异步方法

  this.GetTasksCommand = new DelegateCommand(this.GetTasks); void GetTasks() { GetTasksAsync(); } 

现在我的测试方法就像

  [TestMethod] public void Command_Test_GetTasksCommand() { MyViewModel.GetTaskCommand.Execute(); // this should populate ViewModel.SimpleTask Assert.IsTrue(MyBiewModel.SimpleTask != null) } 

目前我得到的是我的ViewModel.SimpleTask = null这是因为它不等待异步方法完成。

我编写了一个AsyncCommand类,它从Execute方法返回一个Task对象。 然后,您需要显式实现ICommand.Execute ,等待Execute实现中的Task:

 public class AsyncCommand : ICommand { public event EventHandler CanExecuteChanged; public Func ExecutedHandler { get; private set; } public Func CanExecuteHandler { get; private set; } public AsyncCommand(Func executedHandler, Func 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; } } 

在unit testing中,您只需等待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中你可以尝试:

 ParsingCommand = new DelegateCommand(async (x) => await StartParsing(x)); 

在Prism 6中,您可以从异步处理程序创建DelegateCommandDelegateCommand

例如:

startParsingCommand=DelegateCommand .FromAsyncHandler(StartParsingAsync,CanStartParsing) .ObservesProperty(()=> IsParserStarted);