用Mockito嘲笑比特流读者

时间:2016-03-31 08:45:56

标签: java unit-testing mockito

我目前正在调试一个相当复杂的算法来修复比特流中的错误。 BitReader界面非常简单,主要阅读方法如下:

/**
  Reads bits from the stream.
  @param length number of bits to read (<= 64)
  @return read bits in the least significant bits
*/
long read(int length) throws IOException;

目标是测试BitStreamFixer是否实际修复了流(在这里难以描述的方式)。基本上我需要为它提供“损坏的”输入并测试它的输出是否正确(某些输入无法完全修复),如下所示:

BitStreamFixer fixer = new BitStreamFixer(input);
int word1 = fixer.readWord();
int word2 = fixer.readWord();
// possibly a loop here
assertEquals(VALID_WORD1, word1);
assertEquals(VALID_WORD2, word2);
// maybe a loop here too

现在,BitStreamFixer类接受BitReader的实例。单元测试修复器时,我显然需要一个这样的实例。但是我从哪里得到一个呢?我有两个明显的选择:要么给它一个真实的BitReader实现,要么嘲笑它。

前一个选项并不具有吸引力,因为它会对另一个与被测试类无关的对象创建依赖关系。此外, 要做的事。

后一种选择看起来更好,适合通常的单元测试方法。但是,由于我甚至不知道修复者将给BitReader提供什么参数,因此嘲笑它并不容易。我必须采用read方法,实现一个自定义的答案,这个答案将创建许多有点笨拙的逻辑,用适当的位来填充测试中的对象,无论它要求的大小是多少。考虑到我使用的比特流具有相当复杂的高级结构,这并不容易。在单元测试中引入逻辑也并不好闻。

您怎么看?还有其他选择吗?或者也许其中一个可以通过我没注意到的方式得到改善?

1 个答案:

答案 0 :(得分:1)

编写,测试并使用明确的可重用测试帮助程序。

从一般意义上讲,在单元测试中,您应该通过观​​察系统与您有信心的系统进行成功交互来建立对系统的信心。当然,您还希望系统快速,确定,易于阅读/修改,但最终这些系统仍然是系统工作的断言。

您列出了两个选项:

  • 使用模拟BitReader,您有足够的信心预测系统的互动,您可以设置整个&#34;当A然后B&#34;会话。当你有一个独立方法的小型API表面(如RPC层)时,模拟可能非常简单,但是如果有一个具有不可预测的方法调用的有状态对象,则模拟可能非常困难。模拟对确定性存根非确定性系统(如外部服务器或伪随机源)或尚不存在的系统更有用;这些都不适合你。

    由于您的read方法可以采用各种参数,每个参数都有效并且会更改系统的状态,因此在此处使用模拟可能不是一个明智的想法。除非BitStreamFixerBitReader的调用顺序足够确定以构成其合同的一部分,否则模拟BitReader可能会导致一个脆弱的测试:一个在实现更改时中断即使系统功能完善。你想要避免这种情况。

    请注意,模拟不应该产生复杂的逻辑&#34;,只能进行复杂的设置。您正在使用模拟来避免在测试中使用真实逻辑。

  • 使用真实的BitReader,这听起来像构造会很痛苦和不透明。这可能是最现实的解决方案,特别是如果您已经完成了编写和测试的话。

    您担心&#34;引入新的依赖关系&#34;,但如果您的BitReader实现存在并且快速,确定性且经过充分测试,那么您不应该感到任何使用它比在测试中使用真正的ArrayList或ByteArrayInputStream更糟糕。听起来这里唯一真正的问题是创建字节数组会使测试难以维持,这是一个有效的考虑因素。

在评论中,真正的答案是:构建你遗失的BitWriter

@Test public void shouldFixBrokenStream() {
  BitReader bitReader = new StreamBitReader(BitWriter.create()
      .pushBits(16, 0x8080)
      .pushBits(12, 0x000)   // invalid 12-bit sequence
      .pushBits(16, 0x8080)
      .asByteArrayInputStream());
  BitStreamFixer fixer = new BitStreamFixer(bitReader);
  assertEquals(0x80808080, fixer.read(32));
}

/** Of course, you could skip the BitReader yourself, and just make a new one. */
@Test public void shouldFixBrokenStream_bitReader() {
  BitReader bitReader = new InMemoryBitReader();
  bitReader.pushBits(16, 0x8080);
  bitReader.pushBits(12, 0x000);   // invalid 12-bit sequence
  bitReader.pushBits(16, 0x8080);

  BitStreamFixer fixer = new BitStreamFixer(bitReader);
  assertEquals(0x80808080, fixer.read(32));
}

这比离线构建不透明比特流并将其复制粘贴到您的测试中更具可读性(特别是如果评论得很好),比模拟更不易碎,并且比自己的内部更加可测试 class(或基于Answer的版本)。您可能也可以在多个测试用例中使用这样的系统,甚至可能使用多个测试。