2012-05-11 31 views
13

私は実行し、非同期操作することを持っている方法:ユニットテストの非同期操作

Task.Factory.StartNew(() => 
     { 
      // method to test and return value 
      var result = LongRunningOperation(); 
     }); 

は、私は(C#で書かれた)私のユニットテスト中など、必要なメソッドをスタブが、問題があることですテストをアサートする前に非同期操作が完了していません。

どうすればこの問題を回避できますか? TaskFactoryのモックを作成するか、非同期操作をテストするためのヒントを作成する必要がありますか?

答えて

6

あなたはタスク作成を偽装する何らかの方法が必要です。

Task.Factory.StartNew呼び出しを依存関係(ILongRunningOperationStarter)に移動した場合は、TaskCompletionSourceを使用して、必要な場所に正確に完了するタスクを作成する代替実装を作成できます。

少し毛むくじゃらするかもしれませんが、になります。私はblogged about thisしばらく前に - ユニットテストをのタスクを受け取りましたが、もちろん簡単になりました。これはC#5のasync/awaitのコンテキストにありますが、同じ原則が適用されます。

タスクの作成全体を偽造したくない場合は、タスクファクトリを置き換え、そのタイミングを制御することができます。正直言って、それはもっと毛がいっぱいでしょう。

+0

public TaskScheduler TaskScheduler { get { return taskScheduler ?? (taskScheduler = TaskScheduler.Current); } set { taskScheduler = value; } } public TaskScheduler TaskSchedulerUI { get { return taskSchedulerUI ?? (taskSchedulerUI = TaskScheduler.FromCurrentSynchronizationContext()); } set { taskSchedulerUI = value; } } public Task Update() { IsBusy = true; return Task.Factory.StartNew(() => { LongRunningTask(); }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler) .ContinueWith(t => IsBusy = false, TaskSchedulerUI); } 

あなたはユニットテスト以下書きます:

public class CurrentThreadTaskScheduler : TaskScheduler { protected override void QueueTask(Task task) { TryExecuteTask(task); } protected override bool TryExecuteTaskInline( Task task, bool taskWasPreviouslyQueued) { return TryExecuteTask(task); } protected override IEnumerable<Task> GetScheduledTasks() { return Enumerable.Empty<Task>(); } public override int MaximumConcurrencyLevel => 1; } 

だから、コードをテストするために:著者への参照を逃すためのコードの下

は、インターネットからコピーされた、申し訳ありませんhttps://codeblog.jonskeet.uk/2011/11/25/eduasync-part-17-unit-testing/ – luviktor

+0

@luviktor:修正済み、ありがとうございます。 (次回はいつでもあなた自身で編集を提案することができます) –

0

は、私はユニットテストのための特別な実装とあなたの方法でTaskSchedulerをスタブすることを提案するだろう

object result = null; 
Task t = Task.Factory.StartNew(() => result = LongRunningThing()); 


Task.Factory.ContinueWhenAll(new Task[] { t },() => 
{ 
    Debug.Assert(result != null); 
}); 
2

...このような何かを試してみてください。あなたが現在のスレッドで新しいタスクを実行するためにDeterministicTaskSchedulerがthis blog postで説明し使用することができ、あなたのユニットテストでは

private TaskScheduler taskScheduler; 

public void OperationAsync() 
{ 
    Task.Factory.StartNew(
     LongRunningOperation, 
     new CancellationToken(), 
     TaskCreationOptions.None, 
     taskScheduler); 
} 

:あなたは、注入されたTaskSchedulerを使用するようにコードを準備する必要があります。

[Test] 
public void ShouldExecuteLongRunningOperation() 
{ 
    // Arrange: Inject task scheduler into class under test. 
    DeterministicTaskScheduler taskScheduler = new DeterministicTaskScheduler(); 
    MyClass mc = new MyClass(taskScheduler); 

    // Act: Let async operation create new task 
    mc.OperationAsync(); 
    // Act: Execute task on the current thread. 
    taskScheduler.RunTasksUntilIdle(); 

    // Assert 
    ... 
} 
0

設定UIやバックグラウンドタスクschedularsと、この1でユニットテストでそれらを置き換える:あなたが最初のassert文を打つ前に、あなたの「非同期」操作が完了します。

[Test] 
public void WhenUpdateThenAttributeManagerUpdateShouldBeCalled() 
{ 
    taskScheduler = new CurrentThreadTaskScheduler(); 
    viewModel.TaskScheduler = taskScheduler; 
    viewModel.TaskSchedulerUI = taskScheduler; 
    viewModel.Update(); 
    dataManagerMock.Verify(s => s.UpdateData(It.IsAny<DataItem>>())); 
} 
関連する問題