2017-03-22 5 views
0

目的なぜこの非同期ユニットテストはスレッドを永遠にブロックしますか?

私は、カスタム拡張メソッドでTask<T>を使用してC#で非同期ワークフローを構成します。私は彼らが正しく動作することを保証するために使用する基本的な拡張メソッドをそれぞれ単体テストしたいので、一緒に組み立てるときに動作に驚きはありません。このテストの最も重要な側面の1つは、ワークフローが作成されているときではなく、作成されたワークフローが待っているときにのみ、これらのメソッドが必要なときにタスクを実行するようにすることです。

実際には、私の拡張メソッドが正常に動作しているようです。しかし、私はテストスレッドをブロックせずに、これらのメソッドの待っている側面をテストする単体テストを作成することができませんでした。私は、Visual Studio 2015を使用してい

、C#5、.NET 4.5.1、およびNUnitの3

コード

ここでテストに私が望むそのような拡張メソッドです:

は、
public static async Task<T> Let<T>(this Task<T> source, Action<T> action) 
{ 
    var result = await source; 
    action(result); 
    return result; 
} 

私はこの方法で行ったテストです。 (あなたがAssert.AreEqual(3, x)を意味するx.ShouldBe(3)を書くことができるように、このプロジェクトは、NUnitのための流れるようなインターフェイスを提供しShouldlyを使用しています。)

[TestFixture, Category("Task")] 
public class WhenLettingTask { 

    private static bool sourceExecuted; 
    private static int sideEffectResult; 

    private static Task<int> Source => 
     new Task<int>(() => { 
      sourceExecuted = true; 
      return 1; 
     }); 

    private static readonly Action<int> SideEffect = 
     n => { sideEffectResult = n; }; 

    [SetUp] 
    public void TestSetup() { 
     sourceExecuted = false; 
     sideEffectResult = 0; 
    } 


    [Test] 
    public void ShouldNotExecuteAnythingImmediately() { 
     var composed = Source.Let(SideEffect); 

     sourceExecuted.ShouldBeFalse(); 
     sideEffectResult.ShouldBe(0); 
    } 

    [Test] 
    public async Task ReturnedTaskShouldExecuteInputsOnlyOnce() { 
     var composed = Source.Let(SideEffect); 

     var result = await composed; //Blocks forever 
     sourceExecuted.ShouldBeTrue(); 
     sideEffectResult.ShouldBe(1); 

     sourceExecuted = false; 
     sideEffectResult = 0; 

     for (var i = 0; i < 10; i++) { 
      result = await composed; 

      sourceExecuted.ShouldBeFalse(); 
      sideEffectResult.ShouldBe(0); 
     } 
    } 
} 

最初のテストでは、永遠に最初awaitで期待どおりに動作しますが、2番目の1つのブロック。私は、テスト対象のメソッド内awaitを削除する場合

研究

興味深いことに、テストはブロックされません。

非同期テストに関するヘルプを探して
public static async Task<T> Let<T>(this Task<T> source, Action<T> action) 
{ 
    T result = default(T); 
    action(result); 
    return result; 
} 

、私は仕事に非同期テストを取得するTask.FromResultの使用を推奨する記事をたくさん見てきたが、Task<T>が作成したので、これは、基本的に短絡待っている側面をありますこの方法は、RanToCompletionの状態から始まり、決して待つ必要はありません。

解像度 スコット・チェンバレンからの回答をもとに、私はこれに私のテスト・フィクスチャにSourceプロパティを変更:

private static Task<int> Source => 
    Task.Run<int>(() => { 
     Thread.Sleep(1000); 
     sourceExecuted = true; 
     return 1; 
    }); 

Task.Run私の第二の試験に合格することを可能にする、すぐにタスクを開始します。最初のテストが引き続き行われるように、Thread.Sleepが必要です。

答えて

4

タスクを開始することはありません。あなたは「コールドタスク」として知られています。待ち時間はタスクを開始せず、完了するのを待つだけです。

タスクスケジューラを作成している場合を除き、Task.Run (を使用して、関数が返された時点で既に実行中のホットタスクを作成する必要はありません。new Task (に電話する必要はありません。

関連する問題