用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什么参数,所以嘲笑它并不容易。我将不得不采用when(bitReader.read(anyInt())).thenAnswer(...)方法,实现一个自定义答案,该答案将创建大量的比特篡改逻辑,以将适当的比特以任意大小的块形式舀给被测对象。考虑到我正在处理的比特流具有相当复杂的高级结构,这并不容易。在单元测试中引入逻辑也不太好。

你觉得呢,还有别的选择吗?或者,也许其中一个可以以我没有注意到的方式得到改进?

编写、测试和使用一个清晰的可重用测试助手

在一般意义上,在单元测试中,您应该通过观察系统与您确实信任的系统成功交互来建立对系统的信心。当然,您也希望系统快速、确定,并且易于读取/修改,但最终这些都是次要的,因为您的系统可以工作。

您列出了两个选项:

  • 使用模拟BitReader,在这里你有足够的信心预测系统的交互,从而可以建立整个"当a然后B"的对话。当你有一个独立方法的小API表面(如RPC层)时,模仿可能非常容易,但当你有不可预测的方法调用的有状态对象时,模仿会非常困难。Mocking对于确定性地截断非确定性系统(如外部服务器或伪随机源,或还不存在的系统)更有用;这些对你来说都不是。

    因为您的read方法可以采用各种各样的参数,每个参数都是有效的,并且可以更改系统的状态,所以在这里使用mocking可能不是一个明智的想法。除非BitStreamFixerBitReader的调用顺序具有足够的确定性,可以作为其合同的一部分,否则模拟BitReader很可能会导致一个脆弱的测试:即使系统完全正常工作,当实现发生变化时,测试也会中断。你会想避免的。

    请注意,嘲讽永远不应该产生"复杂的逻辑",而应该产生复杂的设置。您使用mock是为了避免在测试中使用真正的逻辑。

  • 使用一个真正的BitReader,这听起来会很痛苦,而且构建起来不透明。不过,这可能是最现实的解决方案,尤其是如果你已经完成了它的编写和测试

    你担心"引入新的依赖项",但如果你的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));
}

这比离线构建不透明的比特流并将其复制粘贴到测试中更具可读性(尤其是在评论良好的情况下),比mock更不脆弱,而且本身比匿名内部类(或基于Answer的版本)更具可测试性。也有可能您可以在多个测试用例中使用这样的系统,甚至可能使用多个测试。

相关内容

  • 没有找到相关文章

最新更新