单元测试多线程代码时出现问题

时间:2011-01-03 00:41:36

标签: c# java multithreading unit-testing testing

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

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

  1. 调用线程调用Dequeue()并且队列为空。它会阻塞线程,直到有一个要检索的项目。
  2. 调用线程调用Enqueue()并且队列已满。它将阻塞线程,直到再次有空间插入数据。
  3. 我最初的想法是,不是让Pipe类实例化BlockingQueue,而是通过构造函数注入将IQueue的实例传递给它。这样,在测试时,我会传递一个NonBlockingQueue的实例,所以我不必为线程问题而烦恼(当我为Pipe进行单元测试时class和其他使用Pipes的类我只想在队列中添加内容,而不必考虑它们是否已经完整等等。

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

    • BlockingQueue中,如果队列是 空,你试图检索 它的一些东西,它会阻止直到 它有所收获。在一个 NonBlockingQueue它只会呕吐 例外。

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

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

    对此更合适的方法是什么?

    编辑到Mitch:

    这用于实现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);
    

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

2 个答案:

答案 0 :(得分:1)

单元测试的想法是测试一小部分代码,并且最终测试所有代码。有一个原因,它被分解成碎片,一次测试一件比同时测试一切更容易。在代码中你使用BlockingQueue,并且在测试中使用NonBlockingQueue,它引入了各种具有挑战性的方面,这违背了单元测试的目的......在测试中你应该简化,而不是复杂化。那么为什么不在那里使用BlockingQueue呢?您声明线程可能是一个问题,但至少使用一个简单的IQueue实现,它从外部工作就像一个BlockingQueue,但没有线程问题。在单元测试中,您应该能够在IQueue中提供不会抛出异常的实例。例如,由于队列为空,而不是通常抛出异常,而是提出一个新项目。在测试中允许这样做....

答案 1 :(得分:1)

你想让构造函数注入IQueue的实现是正确的。

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

  1. IQueue实施
  2. Pipe责任(除了与IQueue实施互动之外)
  3. PipeIQueue实施的互动
  4. 你想专注于第3点。我认为你需要在这里考虑责任。谁负责确保队列被阻止。这是[{1}}实施的责任,而不是IQueue。因此,Pipe契约将只有一个'动作'(一个方法调用),当我们遇到异常情况(从空队列中出队或添加到一个完整队列)时就会发生这种情况。您希望对此交互进行单元测试,这意味着只测试在异常情况下是否调用操作方法。