目的なぜこの非同期ユニットテストはスレッドを永遠にブロックしますか?
私は、カスタム拡張メソッドで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
が必要です。