【问题标题】:How to test that Dispose() waits for processing to finish?如何测试 Dispose() 等待处理完成?
【发布时间】:2021-11-30 22:23:12
【问题描述】:

我有一个类ProcessingQueue,它有一个void Enqueue(Item item) 方法并且还实现了IDisposable。该类的Dispose() 方法保证一旦返回,所有入队的项目都会被处理。

如何对这个保证进行单元测试?

如果在我的单元测试中我只是调用 Dispose() 然后检查所有项目是否都已处理,这没有任何意义,因为我可能很幸运,甚至在调用 Dispose() 之前所有项目都已处理。

【问题讨论】:

  • 您能否模拟处理逻辑并在那里添加某种等待或同步,以保证在您调用 Dispose 之前模拟处理不会启动?
  • 您无法通过单元测试证明不存在线程竞争错误。使用可变机器负载(启动额外线程)和可变时间(随机 for 循环延迟),通过运行数天的扩展测试获得信心。仍然不能证明缺席,但会降低出现意外的几率。
  • @HansPassant,明白了,没有办法 100% 证明此类程序的正确性。但是是否至少可以编写一个测试来证明,比如说,只有一种场景,这样测试就不会依赖延迟、等待和任何其他时间安排?
  • 不,随机时间对于清除并发错误至关重要。关于这种测试的一个不错的视频是here

标签: c# multithreading dispose idisposable


【解决方案1】:

如果您想要测试的确定性行为,那么对多线程代码进行单元测试会遇到很多困难。通常涉及大量的 ManualResetEvent 来控制控制流。

但是,假设您可以控制项目内部的处理,它应该仍然是可能的。例如

myProcessingQueue.Enqueue(() => releaseTask.WaitOne());

var disposeTask = Task.Run(() => myProcessingQueue.Dispose());

// the dispose task should not complete since
// the task is blocked on releaseTask.WaitOne()
Assert.IsFalse(disposeTask.Wait(100));  

// Release the task to allow the dispose method to complete
releaseTask.Set();

// wait to ensure the dispose actually does complete
Assert.IsTrue(disposeTask.Wait()); 

这并不是一个完全的保证,因为 disposeTask 可能在 101 毫秒而不是 100 毫秒之后(错误地)完成,但我不确定如何解决,甚至是否可以解决该问题。但我希望这种方法至少在某些情况下有用。

【讨论】:

  • 感谢您的回答!我有这个想法。然而,依赖时间不是最终得到片状测试的有保证的方法吗?正如您所提到的,时间总是有可能不会导致您期望的一系列呼叫。即使这样的概率是 0.000001,如果你有 1000 个这样的测试并且你每周运行它们 1000 次,你平均每周都会有一次意外的测试失败。
  • @yaskodev,是的,依赖时间可以导致不稳定的测试。在这个特定的例子中,时间依赖性只能导致假阴性,即一个不应该通过但确实通过的测试。最后,您必须决定什么值得进行单元测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多