2012-11-09 3 views
12

はので、私はいくつかのコードユニットテストコード。ContinueWith()

Task.Factory.StartNew(() => this.listener.Start()).ContinueWith(
        (task) => 
         { 
          if (task.IsCompleted) 
          { 
           this.status = WorkerStatus.Started; 
           this.RaiseStatusChanged(); 
           this.LogInformationMessage("Worker Started."); 
          } 
         }); 

を持っている(namley this.listener.Start( ))。問題は、ContinueWithを呼び出す前にテストが実行を終了することです。私がコードをステップ実行する余分な遅延のため、私はそれをうまく呼び出すデバッグするとき。

別のアセンブリのテストコードから、私のテストがアサートする前にコードが実行されていることを確認するにはどうしたらいいですか?

私はちょうどThread.Sleepを使うことができました...しかし、これは本当にハックのようなものです。

私はThread.Joinのタスクバージョンを探していますね。

+0

はどのようにそれが終了しない

// ... test code to execute the code under test // run the tasks on the ThreadPool scheduler testSchedulers.ThreadPool.Start(); // assertion code can now run 
、何らかのエラーがありますか? – maximpa

+0

エラーはありません... listener.startは嘲笑され、終了します。私はそれをデバッグモードで確認できます。それはタイミングの問題です –

+1

私はそれがむしろ同期の問題であると言うでしょう、あなたはあなたが呼び出す非同期メソッドからいくつかのフィードバックを得る必要があります – maximpa

答えて

2

は、次のことを考えてみましょう:

public class SomeClass 
{ 
    public void Foo() 
    { 
     var a = new Random().Next(); 
    } 
} 

public class MyUnitTest 
{ 
    public void MyTestMethod() 
    { 
     var target = new SomeClass();   
     target.Foo(); // What to assert, what is the result?.. 
    } 
} 

aに割り当てられた値とは何ですか?結果がメソッドFoo()(戻り値、パブリックプロパティ、イベントなど)の外に返されない限り、あなたは知ることができません。

coordinating the actions of threads for a predictable outcome」の処理は、Synchronizationと呼ばれます。あなたのケースでは、最も簡単な解決策の

一つはTaskクラスのインスタンスを返すためにあるかもしれないし、そのWait()メソッドを使用します。

var task = Task.Factory.StartNew(() => Method1()) 
    .ContinueWith(() => Method2()); 

​​は継続を作成するため、最初のタスクを待つ必要がありませんこと対象タスクが完了MSDN)非同期に実行されます。私はDの容易な、まだ実用的な方法があるとは思わない

task.Wait(); 
+1

あなたは彼の問題を誤解していると思いますか? 'ContinueWith'メソッドが呼び出される前に初期タスクが完了したため、彼の継続タスクは実行されていないようです。 – davenewza

+0

@davenewza 'ContinueWith()'は、対象のタスクが完了したときに非同期に実行される継続を作成するため、最初のタスクを待つ必要はありません。 – maximpa

+3

テストからタスクインスタンスにアクセスできず、その目的のためだけに(しかし、私はする必要があります)。 –

1

処理が終了したときに通知する方法がある場合(そのStatusChangedイベントのハンドラを追加できますか?)、ManualResetEventを使用して、妥当なタイムアウトで待機します。タイムアウトが切れた場合はテストに失敗し、それ以外の場合はアサーションを実行します。

など。

var waitHandle = new ManualResetEvent(false); 
sut.StatusChanged += (s, e) => waitHandle.Set(); 

sut.DoStuff(); 

Assert.IsTrue(waitHandle.WaitOne(someTimeout), "timeout expired"); 
// do asserts here 
+0

私は思っていたような気がします。しかし、一般的な問題で私を助けません! –

+0

彼は理解してくれます。あなたは、代わりにThread.Sleepを検討していました。このように動作しますが、通知があった場合に処理が終了するとすぐに処理が続行されます。 Thread.Joinに相当するのはTask.Waitですが、テストしているメソッドにタスクを公開していないと言ってきました。 – fsimonazzi

+0

。たぶん私は不可能な状況を設定しているかもしれません。私は答えとしてあなたをマークします;)...一般的な答えは今、私が必要なものを行うことができるように、タスクのものの周りに何らかのラッパーを書くようです。私は実際に車輪を再発明しようとしていないことを確認しています –

0

ContinueWith()コールの前に初期タスクが完了したかどうかにかかわらず、継続タスクは実行されます。私は次の点を確認しました:

// Task immediately exits 
var task = Task.Factory.StartNew(() => { }); 

Thread.Sleep(100); 

// Continuation on already-completed task 
task.ContinueWith(t => { MessageBox.Show("!"); }); 

さらにデバッグしてください。多分、あなたの仕事は失敗しているでしょう。

+0

これは私が求めているものではありません。私はあなたが余分な考えとして継続を追加することができたことを理解していないが、あなたがこれを行うことができます興味深い!私の仕事は嘲笑されて何もしていないので、失敗することはできません...実際にあなたと同じことをMoqプロキシ経由で実行しています。また、thread.sleepはまさに私がやっていることを避けようとしているものです。問題は、tereは実行中のタスクと継続中の実行との間の遅延であり、この遅延は残りのテストの実行時間よりも長いことです。明示的なスレッドでできることと同等の処理を行い、終了を待ってから再結合したいと思います。 –

2

これをする。私は今すぐ同じ問題に遭遇し、Thread.Sleep(X)は問題を回避するための最も単純な(あまりエレガントではない)方法です。

私が検討した唯一の解決策は、Test.Factory.StartNew()呼び出しをテストの擬似的なインターフェイスの背後に隠して、テストシナリオでタスクの実際の実行を完全に取り除くことですインタフェースメソッドが呼び出されることを期待します。

public interface ITaskWrapper 
{ 
    void TaskMethod(); 
} 

そして、あなたの具体的な実装:たとえば

public class MyTask : ITaskWrapper 
{ 
    public void TaskMethod() 
    { 
     Task.Factory.StartNew(() => DoSomeWork()); 
    } 
} 

それからちょうど模擬テスト方法でITaskWrapperTaskMethodに期待を設定すると呼ばれています。

+0

ええ、これは私が問題にも対処する方法です。どんな意味のあるテストを可能にするために私が見ることができる唯一の方法でしたか? –

+0

これはまた著者のここで使用されている方法です:http://www.codeproject.com/Tips/706553/Mocking-Task-Factory-StartNew – Appulus

0

Reactive Extensionsを使用するテスト中のコードで非同期プロセスを処理する場合、1つの方法はTestSchedulerを使用することです。 TestSchedulerは時間内に前に移動し、細かく処理されたタスクなどを排除できます。テスト対象のコードでは、TestSchedulerインスタンスを提供するISchedulerを使用できます。その後、実際にスリープしたり、待ったり、同期したりすることなく、時間を操作できます。このアプローチの改良点はLee Campbell's ISchedulerProviderです。

コードでTask.Factory.StartNewの代わりにObservable.Startを使用する場合は、ユニットテストでTestSchedulerを使用して、スケジュールされたすべてのタスクをプッシュすることができます。

//Task.Factory.StartNew(() => DoSomething()) 
// .ContinueWith(t => DoSomethingElse()) 
Observable.Start(() => DoSomething(), schedulerProvider.ThreadPool) 
      .ToTask() 
      .ContinueWith(t => DoSomethingElse()) 

とあなたのユニットテストで:

例えば、テスト対象のコードはこのような何かを見ることができる

+0

これは面白そうです。私はこの問題に再び遭遇したとき、私は来ることを忘れないでしょうDIは似たようなことをしました。 –

+0

この回答にも同様のアプローチがあります:http://stackoverflow.com/a/17844870/114200タスクスケジューラを使用し、独自のスケジューラを実装する点が異なります。上記のアプローチが好きですが、Rxをまだ使用していない場合は、タスクスケジューラのアプローチにも関心があります。 –

関連する問題