我已经实现了一个Pipe
类,它在内部使用BlockingQueue
来存储它接收的数据。
BlockingQueue
阻止调用线程有两种情况:
Dequeue()
并且队列为空。它会阻塞线程,直到有一个要检索的项目。Enqueue()
并且队列已满。它将阻塞线程,直到再次有空间插入数据。我最初的想法是,不是让Pipe
类实例化BlockingQueue
,而是通过构造函数注入将IQueue
的实例传递给它。这样,在测试时,我会传递一个NonBlockingQueue
的实例,所以我不必为线程问题而烦恼(当我为Pipe
进行单元测试时class和其他使用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);
所以我想是的,阻塞管道/队列是我想要的行为。
答案 0 :(得分:1)
单元测试的想法是测试一小部分代码,并且最终测试所有代码。有一个原因,它被分解成碎片,一次测试一件比同时测试一切更容易。在代码中你使用BlockingQueue,并且在测试中使用NonBlockingQueue,它引入了各种具有挑战性的方面,这违背了单元测试的目的......在测试中你应该简化,而不是复杂化。那么为什么不在那里使用BlockingQueue呢?您声明线程可能是一个问题,但至少使用一个简单的IQueue实现,它从外部工作就像一个BlockingQueue,但没有线程问题。在单元测试中,您应该能够在IQueue中提供不会抛出异常的实例。例如,由于队列为空,而不是通常抛出异常,而是提出一个新项目。在测试中允许这样做....
答案 1 :(得分:1)
你想让构造函数注入IQueue
的实现是正确的。
现在你想在这里进行单元测试?
IQueue
实施Pipe
责任(除了与IQueue
实施互动之外)Pipe
和IQueue
实施的互动你想专注于第3点。我认为你需要在这里考虑责任。谁负责确保队列被阻止。这是[{1}}实施的责任,而不是IQueue
。因此,Pipe
契约将只有一个'动作'(一个方法调用),当我们遇到异常情况(从空队列中出队或添加到一个完整队列)时就会发生这种情况。您希望对此交互进行单元测试,这意味着只测试在异常情况下是否调用操作方法。