2013-03-31 12 views
11

ユニットテストMVVMを初めて使用し、プロジェクトでPRISMを使用しています。私は現在のプロジェクトで単体テストを実装しており、非同期メソッドを呼び出すDelegateCommandがどれほど完全であるかを私に教えてくれるリソースをオンラインで見つけることはできません。これは、MVVMの非同期メソッドを単体テストする方法について、私のポストの投稿に続く質問です。非同期メソッドをasync TestMethodを使用してテストできると回答しています。How to Unit Test a ViewModel with async method.このシナリオは、テストするメソッドがパブリックメソッドである場合にのみ機能します。ユニットのテスト方法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 
       } 
      } 
     } 

また、ここに私のコードです

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) 
     } 
ようになり、私のVMでコマンドです

現在、私はViewModel.SimpleTask = nullを取得しています。なぜなら、Asyを待たずにncメソッドを終了します。

答えて

18

私はExecuteメソッドからTaskオブジェクトを返すAsyncCommandクラスを作成しました。次に、あなたのExecute実装からタスクを待って、明示的にICommand.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(); 
    } 
} 

あなたは、コマンドクラスにタスクを返すメソッド非同期(async)渡すことができます。あなたのユニットテストで

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; 
    } 
} 

を、あなたは

[TestMethod] 
public async Task TestAsyncCommand() 
{ 
    var viewModel = new ViewModel(); 

    Assert.IsFalse(viewModel.Executed); 
    await viewModel.AsyncCommand.Execute(); 

    Assert.IsTrue(viewModel.Executed); 
} 

UIが、一方で、明示的に実装を呼び出します:単にExecuteメソッドを待ちますタスクを待つ世話をする方法。

(*)その間、私は、共通の命名規則に従えば、タスクを返すメソッドは実際にはExecuteAsyncという名前になっていることに気付きました。

+1

TaskCommandのHTTPSの私の実現を見てみましょう: //github.com/Catel/Catel/blob/develop/src/Catel.MVVM/Catel.MVVM.NET40/MVVM/Commands/TaskCommand.csと使用例https://github.com/Catel/Catel.Examples /tree/master/src/NET/Catel.Examples.WPF。TaskCommand –

+0

こんにちは@AlexanderLogger、私はCatelのTaskCommandがCatelのフレームワークの大部分として優れた実装であることに同意しますが、単体テストでVM.MyTaskCommand.Executeを使用するための適切な待機時間をまだ把握していません。 AFAIKは、Taskオブジェクトの代わりにvoidを返すためです。 Awaitを使用して適切な例外伝播を得ることができます。 voidを返すので、最終的な例外を取り除くだけです。ですから、ダニエルが提案したAsyncCommandが好きなのです。それでも、私はCatel側で何かを逃しているかもしれないので、もし私が間違っていたら、教えてください。 –

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

関連する問題