[英]How to Unit Test DelegateCommand that calls async methods in MVVM
我是單元測試MVVM的新手,並在我的項目中使用PRISM。 我正在我們當前的項目上實現單元測試,而沒有運氣在線查找資源,這會告訴我如何調用異步方法的totest DelegateCommand。 這是我的帖子的后續問題 - 如何使用異步方法對ViewModel進行單元測試。 關於如何在MVVM中單元測試異步方法,並回答公共方法可以使用異步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中的命令,它調用上面的異步方法
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<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中你可以嘗試:
ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x));
在Prism 6中,您可以從異步處理程序創建DelegateCommand
和DelegateCommand<T>
。
例如:
startParsingCommand=DelegateCommand .FromAsyncHandler(StartParsingAsync,CanStartParsing) .ObservesProperty(()=> IsParserStarted);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.