【问题标题】:How to isolate a void print() method for testing如何隔离用于测试的 void print() 方法
【发布时间】:2023-03-27 23:27:01
【问题描述】:

我编写了一个长除法应用程序,现在我想对其进行测试。 稍微简化一下,我有:Classes simplified

  1. InputData 类包含两个长整数:被除数和除数。 InputData 实例提供给 Division。
  2. 创建实例除法后,我对其运行 divide() 方法以生成 OutputData。
  3. OutputData 包含有关除法过程的每个阶段的所有信息以及结果的元数据(整数与否、周期性与否等)
  4. 最后 OutputData 是我要测试的 printer.print() 的输入。

printer output

问题是如何正确使用mocks\stubs\spies来测试printer类的void print方法?我的老师说我不能使用除 outputData 和 OutputStream 之外的任何变量来测试打印机,并建议阅读有关 mockito 的内容。

假设我想用这些输入测试打印机输出的正确性:-943 和 225。我的非隔离版本如下所示:

@Test
void testPrintDivisionNegativePositiveRecurrentExpansion() {
    final long VALID_INPUT_DIVIDEND = -943;
    final long VALID_INPUT_DIVISOR = 225;
    inputData = new InputData(VALID_INPUT_DIVIDEND, VALID_INPUT_DIVISOR);
    Division division = new Division(inputData);
    OutputData outputData = division.divide();
    printer.print(outputData, streamOut);
    String actual = streamOut.toString();
    String expected = "_-943|225\r\n" + " -900|--------\r\n" + " ----|-4.19(1)\r\n" + " _-430\r\n" + "  -225\r\n"
            + "  ----\r\n" + " _-2050\r\n" + "  -2025\r\n" + "  -----\r\n" + "   _-250\r\n" + "    -225\r\n"
            + "    ----\r\n" + "     -25\r\n";
    assertEquals(expected, actual);
}

我阅读了有关模拟和存根的信息,但大多数示例都建议使用 when().then() 并返回简单的字符串或 int,而在我的例子中 outputData 将包含 List 每个包含 5 个字段。 outputData

我有点明白如何为此使用 spy(),但如果我理解正确 spy() 运行实际方法,那么依赖关系仍然存在,并且无法实现完全隔离。那为什么我的测试版本更差?

【问题讨论】:

  • 您不需要 Mockito、InputData、Division 来测试以 OutputData 和 OutputStream 作为参数的 Printer 方法。您只需要一台打印机、一个OutputData 和一个OutputStream。创建打印机和输出数据应该很简单。关于流,最简单的方法是使用一个 ByteArrayOutputStream,调用方法,将流转换为字节数组,看看它是否包含预期的字节。
  • 是的,这就是我使用 streamOut 的方式,它是一个 ByteArrayOutputStream。但是如何创建 OutputData?它应该看起来像这样prntscr.com/ijh959
  • new OutputData(the, required, arguments)? IE。与您在 Division.divide() 方法中创建它的方式相同。
  • 所需的参数不是微不足道的,如prnt.sc/ijh959 所示,它们是在divide() 中计算的。我应该在纸上进行计算然后手动创建 allStages 变量吗?但即便如此,它也将依赖于 Stage() 类。
  • 您可以使用 Division 类来创建一个 OutputData,如果它可以让您的测试变得更简单。核心点是 Mockito 是无关紧要的。使用 ByteArrayOutputStream 作为参数调用您的打印方法,并检查是否已写入预期的字节。

标签: java unit-testing junit mockito


【解决方案1】:

你在测试什么代码?

这是你必须回答的最重要的问题。

任何其他有自己的业务逻辑与您的代码交互的类都应该被模拟。

根据您的测试名称,我猜printer 对象是您的测试代码,OutputData 是提供(针对您的测试用例)不相关业务逻辑的依赖项。因此你应该嘲笑它:

class MyTestClass {
  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 
  @Mock OutputData outputData;
  @Test
  void testPrintDivisionNegativePositiveRecurrentExpansion() {
    // configure mock of OutputData
    printer.print(outputData, streamOut);
    String actual = streamOut.toString();
    String expected = "_-943|225\r\n" + " -900|--------\r\n" + " ----|-4.19(1)\r\n" + " _-430\r\n" + "  -225\r\n"
        + "  ----\r\n" + " _-2050\r\n" + "  -2025\r\n" + "  -----\r\n" + "   _-250\r\n" + "    -225\r\n"
        + "    ----\r\n" + "     -25\r\n";
    assertEquals(expected, actual);
  }
}

我阅读了有关模拟和存根的信息,但大多数示例建议使用 when().then() 并返回简单的字符串或 int,而在我的例子中 outputData 将包含 List 每个包含 5 个字段。

当您的返回类型是复杂的或List 时,模拟没有太大区别。您创建它的一个实例并配置您的模拟以返回它:

class MyTestClass {
  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 
  @Mock OutputData outputData;
  @Test
  void testPrintDivisionNegativePositiveRecurrentExpansion() {
    List<YourElementType> inputData= Arrays.asList(new YourElementType(dummyValues1), new YourElementType(dummyValues2), new YourElementType(dummyValues3)); 
    doReturn(inputData).when(outputData).getValuesToPrint();
    printer.print(outputData, streamOut);
    // ...

【讨论】:

  • 请看一下我的示例 outputData prnt.sc/ijh959 如果我做对了,我应该手动分配这些值对吗?但即使这样 outputData 将是一个 List 因此使用类 Stage.
  • @Andrey “我应该手动分配这些值吗?” 是的。 -- “但即便如此,outputData 仍将是 List&lt;Stage&gt;,因此使用 Stage 类。” 是的。
猜你喜欢
  • 1970-01-01
  • 2020-12-07
  • 2021-08-27
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多