在我的代码中,我有一个方法,例如:
void PerformWork(List<Item> items)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
foreach (var item in items)
{
await itemHandler.PerformIndividualWork(item);
}
});
}
Item
只是一个已知模型,而itemHandler
只是根据模型进行一些工作(ItemHandler
类在单独维护的代码库中定义为nuget pkg,我不想修改)。此代码的目的是在后台同时同步处理列表中的项目。
作为工作的一部分,我想创建一个单元测试,以验证调用此方法时,项目是同步处理的。我很确定这个问题可以简化为:
await MyTask(1);
await MyTask(2);
Assert.IsTrue(/* MyTask with arg 1 was completed before MyTask with arg 2 */);
此代码的第一部分我可以轻松地进行单元测试,即保持了序列。例如,使用NSubstitute,我可以检查库代码上的方法调用顺序:
Received.InOrder(() =>
{
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "First item"));
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "Second item"));
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "Third item"));
});
但是我不太确定如何确保它们不会并行运行。我有几个想法似乎很糟糕,例如嘲笑库,使在调用PerformIndividualWork
时出现人为延迟,然后检查正在排队的整个后台任务的时间,或者检查itemHandler
接收到的调用的时间戳通话之间的最短时间。例如,如果我模拟了PerformIndividualWork
来延迟500毫秒,并且我期望有3个项目,那么我可以检查经过的时间:
stopwatch.Start();
// I have an interface instead of directly calling HostingEnvironment, so I can access the task being queued here
backgroundTask.Invoke(...);
stopwatch.Stop();
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 1500);
但是那感觉不正确,可能会导致误报。解决的办法可能在于修改代码本身。但是,我想不出一种有意义地更改它的方法,以使这种单元测试(测试任务按顺序运行)成为可能。我们肯定会进行系统/集成测试,以确保不会发生由单个项目的异步性能引起的问题,但是我也想在此级别进行测试。
[不确定这是否是一个好主意,但是一种方法可能是使用itemHandler
来检测何时并行处理项目。这是一个简单又肮脏的例子: