【问题标题】:Problem when Unit-testing multi threaded code单元测试多线程代码时的问题
【发布时间】:2011-01-03 00:41:35
【问题描述】:

我已经实现了一个Pipe 类,它在内部使用BlockingQueue 来存储它接收到的数据。

BlockingQueue会阻塞调用线程有两种情况:

  1. 调用线程调用Dequeue(),队列为空。它将阻塞线程,直到有要检索的项目。
  2. 调用线程调用Enqueue(),队列已满。它将阻塞线程,直到有空间再次插入数据。

我最初的想法是,不是让Pipe 类实例化BlockingQueue,而是通过构造函数注入将IQueue 的实例传递给它。这样,在测试时,我会将NonBlockingQueue 的实例传递给它,因此我不必为线程问题而烦恼(当我为Pipe 类和其他类进行单元测试时)使用Pipes 的类我只想将东西添加到队列中,而不必考虑它们是否已经满了等等)。

问题在于,在执行此操作时,我实际上使我的 Pipe 以两种完全不同的方式运行,具体取决于我传递给它的 IQueue 实例的类型:

  • BlockingQueue 中,如果队列是 为空,您尝试检索 来自它的东西,它会阻塞直到 它得到了一些东西。在一个 NonBlockingQueue 它会呕吐 一个例外。

  • BlockingQueue 中,如果队列已满并且您尝试添加某些内容,它将等到有人将元素出列并且再次有空间。 NonBlockingQueue 版本要么抛出 FullQueueException,要么允许“无限”数量的元素。

也就是说,没有“单一合同”。我认为这种做法肯定是错误的。

什么是更合适的方法?

编辑米奇:

这用于实现 Pipe&Filter 系统:每个 Filter 都有一个输入和输出管道。 然后每个过滤器都使用以下形式的代码实现

char c;
while ((c = inputPipe.ReadChar()) != STREAM_TERMINATOR) { 
//I don't have to care 
//if right now there is any data. I know that if there isn't, 
//the thread will block and this will continue after there is some.

    ...do processing
    outputPipe.WriteChar(something);
}
outputPipe.WriteChar(STREAM_TERMINATOR);

所以我想是的,阻塞管道/队列是我想要的行为。

【问题讨论】:

    标签: c# java multithreading unit-testing testing


    【解决方案1】:

    单元测试的理念是您测试一小部分代码,并最终测试所有代码。它被分解成碎片是有原因的,一次测试一件比同时测试所有东西更容易。在代码中使用 BlockingQueue,在测试中使用 NonBlockingQueue,它引入了各种具有挑战性的方面,这违背了单元测试的目的……在测试中,你应该简化,而不是复杂化。那么为什么不在那里也使用 BlockingQueue 呢?您说线程可能是一个问题,但至少使用 IQueue 的简单实现,从外部看它与 BlockingQueue 完全一样,但没有线程问题。在单元测试中,您应该能够在 IQueue 中提供一个不会引发异常的实例。例如,不要因为队列为空而通常抛出异常,而只需提出一个新项目。这在测试中是允许的......

    【讨论】:

    • 我同意你的观点。从理论上讲,您所说的是正确的,但实际上很难对具有这种阻塞队列(而不是)的队列进行单元测试。另一种方法是尽量小心并实际使用阻塞队列进行测试(基本上,如果我看到任何测试挂起,那是因为它已死锁)。
    • 在您的代码中,您有一个 IQueue 的实现,当它为空或满时不会抛出异常。那是您找不到的“单一合同”的描述。它不完全是 IQueue,但类似于它。如果你愿意,给它一个名字。我的观点是,如果您要(简化)该合同的实施,则不会失去一般性。您应该能够在测试中使用这样的实现,并且它不应该给期望带来麻烦。如果有,请详细说明,因为在那种情况下,我还不清楚情况。
    【解决方案2】:

    您对IQueue 的构造函数注入实现的想法是正确的。

    现在你想在这里进行什么单元测试?

    1. IQueue 实现
    2. Pipe 责任(除了与IQueue 实现交互)
    3. PipeIQueue 实现的交互

    您想专注于第 3 点。我认为你需要在这里考虑责任。确保队列被阻塞与否是谁的责任。这是IQueue 实现而不是Pipe 的责任。所以,IQueue 合约只会有一个“动作”(一个方法调用),当我们遇到异常情况(从空队列出队或添加到满队列)时会发生这种情况。您想对这种交互进行单元测试,这意味着只测试在异常情况下是否调用操作方法。

    【讨论】:

      猜你喜欢
      • 2013-04-16
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      相关资源
      最近更新 更多