静的メソッド(この場合はTask.Run
)が含まれているユニットをテストするソリューションでよくあることですが、これをラップする依存関係として何かを渡す必要があります。テストに。
@リーチは答えとして、TaskScheduler
を渡すことでこれを行うことができます。このテスト版では、エンキュー時にタスクの数を維持できます。
保護レベルのため、実際には少し醜いですが、この投稿の末尾には、既存のTaskScheduler
(たとえば、TaskScheduler.Default
を使用することができます)をラップするものが含まれています。
残念ながら、あなたもTaskScheduler.Default
を除いて、basically what Task.Run
does under the hoodある
Task.Factory.StartNew(
() => DoSomething(),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
myTaskScheduler);
のようなものに
Task.Run(() => DoSomething);
のようなあなたの呼び出しを変更する必要があります。あなたはもちろん、どこかでヘルパーメソッドでそれを包むことができます。
あなたがテストコードでいくつかのリスクの高い反射についてうるさいされていない場合、あなたはまだちょうどTask.Run
を使用できるように別の方法として、あなたは、TaskScheduler.Default
プロパティを乗っ取ることができます:
var defaultSchedulerField = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", BindingFlags.Static | BindingFlags.NonPublic);
var scheduler = new TestTaskScheduler(TaskScheduler.Default);
defaultSchedulerField.SetValue(null, scheduler);
(プライベートフィールド名がTaskScheduler.cs line 285からです。 )
したがって、たとえば、このテストは私のTestTaskScheduler
以下と反射トリックを使用して渡します。
[Test]
public void Can_count_tasks()
{
// Given
var originalScheduler = TaskScheduler.Default;
var defaultSchedulerField = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", BindingFlags.Static | BindingFlags.NonPublic);
var testScheduler = new TestTaskScheduler(originalScheduler);
defaultSchedulerField.SetValue(null, testScheduler);
// When
Task.Run(() => {});
Task.Run(() => {});
Task.Run(() => {});
// Then
testScheduler.TaskCount.Should().Be(3);
// Clean up
defaultSchedulerField.SetValue(null, originalScheduler);
}
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
public class TestTaskScheduler : TaskScheduler
{
private static readonly MethodInfo queueTask = GetProtectedMethodInfo("QueueTask");
private static readonly MethodInfo tryExecuteTaskInline = GetProtectedMethodInfo("TryExecuteTaskInline");
private static readonly MethodInfo getScheduledTasks = GetProtectedMethodInfo("GetScheduledTasks");
private readonly TaskScheduler taskScheduler;
public TestTaskScheduler(TaskScheduler taskScheduler)
{
this.taskScheduler = taskScheduler;
}
public int TaskCount { get; private set; }
protected override void QueueTask(Task task)
{
TaskCount++;
CallProtectedMethod(queueTask, task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return (bool)CallProtectedMethod(tryExecuteTaskInline, task, taskWasPreviouslyQueued);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return (IEnumerable<Task>)CallProtectedMethod(getScheduledTasks);
}
private object CallProtectedMethod(MethodInfo methodInfo, params object[] args)
{
return methodInfo.Invoke(taskScheduler, args);
}
private static MethodInfo GetProtectedMethodInfo(string methodName)
{
return typeof(TaskScheduler).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
}
}
またはコメントで@hgcummingsにより示唆されるようRelflectionMagicを使用して片付け:ここ
は、テストタスクスケジューラで
var scheduler = new TestTaskScheduler(TaskScheduler.Default);
typeof(TaskScheduler).AsDynamicType().s_defaultTaskScheduler = scheduler;
using System.Collections.Generic;
using System.Threading.Tasks;
using ReflectionMagic;
public class TestTaskScheduler : TaskScheduler
{
private readonly dynamic taskScheduler;
public TestTaskScheduler(TaskScheduler taskScheduler)
{
this.taskScheduler = taskScheduler.AsDynamic();
}
public int TaskCount { get; private set; }
protected override void QueueTask(Task task)
{
TaskCount++;
taskScheduler.QueueTask(task);
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return taskScheduler.TryExecuteTaskInline(task, taskWasPreviouslyQueued);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return taskScheduler.GetScheduledTasks();
}
}
それは...素晴らしい?恐ろしい?私は本当になぜこれをデフォルトのスケジューラとして使うのかはわかりませんが、あなたはJustDoできることではありませんが、どうやって今できるのか分かりました。 – Brondahl
ニース(またはおそらく悪)! ReflectionMagicのAsDynamicを使用してTestTaskRunnerを少し簡略化することができます(ただし、パフォーマンスが低下する可能性があります)。 https://blogs.msdn.microsoft.com/davidebb/2010/01/18/use-c-4-0-dynamic-to-drastically-implify-your-private-reflection-code/ – hgcummings
ニースを参照してください。私はいくつかのクリーンアップコードを追加して、テストの最後にTaskScheduler.Defaultを再び戻すか、または他の後のテストで望ましくない干渉を受けるかもしれません。 – Rich