我的应用程序中有一个线程模块,用于管理并行操作。由于种种原因,它增加了计时和日志记录的各个方面,而且非常复杂,以至于我最近发现我在一个错误中进行了编码,从而在双嵌套线程上启动了一些任务。
即它调用的等效项是:
Task.Run(
async () => await Task.Run(
() => DoStuff();
);
).Wait()
现在,一方面,该代码起作用了……目标代码被运行,并且等待的代码直到目标代码完成后才继续。
另一方面,它使用2个线程而不是1个线程来执行此操作,并且由于我们在线程饥饿方面遇到问题,因此这是一个问题。
我知道如何修复代码,但我想编写一个单元测试以确保A)我已经修复了所有此类错误,并在所有情况下均对其进行了修复。B)没有人会在将来重现此错误。
但是我看不到如何掌握“我创建的所有线程”。CurrentProcess.Threads给了我线程的负载,并且没有明显的方法来识别我所关心的线程。
有什么想法吗?
正如单元测试通常涉及一种涉及静态方法的解决方案(Task.Run
在这种情况下)一样,您可能需要将某些内容作为依赖项传递给类,以将其包装起来,然后可以在测试中添加行为。
正如@Rich在他的回答中所建议的那样,您可以通过输入来实现TaskScheduler
。这样,您的测试版本可以在排队任务时维护其数量。
TaskScheduler
由于保护级别的缘故,进行测试实际上有点难看,但是在本文的底部,我提供了一个包含现有内容的测试TaskScheduler
(例如,您可以使用TaskScheduler.Default
)。
不幸的是,您还需要更改通话内容,例如
Task.Run(() => DoSomething);
像
Task.Factory.StartNew(
() => DoSomething(),
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
myTaskScheduler);
这基本上是Task.Run
在引擎盖下做的,除了引擎盖TaskScheduler.Default
。您当然可以将其包装在某个地方的辅助方法中。
另外,如果您不担心测试代码中的某些风险较高的反射,则可以劫持该TaskScheduler.Default
属性,以便仍然可以使用Task.Run
:
var defaultSchedulerField = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", BindingFlags.Static | BindingFlags.NonPublic);
var scheduler = new TestTaskScheduler(TaskScheduler.Default);
defaultSchedulerField.SetValue(null, scheduler);
(私有字段名称来自TaskScheduler.cs第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);
}
}
或者收拾使用RelflectionMagic通过在注释中@hgcummings的建议:
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();
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句