2013-03-31 63 views
11

我是新手單元測試MVVM和在我的項目中使用PRISM。我正在對當前項目執行單元測試,並且沒有在線查找資源的運氣,這會告訴我如何調用異步方法的Tatest DelegateCommand。這是關於如何在MVVM中單元測試異步方法的後續問題 - How to Unit Test a ViewModel with async method.,並回答說可以使用異步TestMethod測試公共方法。只有我想測試的方法是公共方法時,此方案纔有效。如何在MVVM中調用異步方法的單元測試DelegateCommand

問題是我想測試我的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這是因爲它不等待asy nc方法完成。

回答

18

我寫了一個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

+1

看看我實現TaskCommand HTTPS的: https://github.com/Catel/Catel.Examples /tree/master/src/NET/Catel.Examples.WPF。TaskCommand –

+0

Hi @AlexanderLogger,雖然我同意Catel的TaskCommand是一個優秀的實現,但作爲Catel的大部分框架,我還沒有想出在單元測試中使用VM.MyTaskCommand.Execute的正確方法。 AFAIK,因爲它返回void而不是Task對象,將允許我使用等待並獲得適當的異常傳播。由於它返回無效,它只是吞下最終的異常。所以,這就是爲什麼我更喜歡Daniel提出的AsyncCommand。儘管如此,如果我錯了,請讓我知道。 –

0

在棱鏡6中,您可以從異步處理程序創建DelegateCommandDelegateCommand<T>

例如:

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

+0

FromAsyncHandler在Prism 6.x中已經過時(我不知道哪個x:D),那麼有沒有更好的方法來做到這一點? –

2

既然不能添加註釋,爲了完整起見,在PRISM 6,你可以嘗試:

ParsingCommand = new DelegateCommand<string>(async (x) => await StartParsing(x)); 
+0

好消息!謝謝=) – Vladislav

相關問題