【问题标题】:Test public method that calls internal methods depending on input测试根据输入调用内部方法的公共方法
【发布时间】:2014-03-11 08:38:51
【问题描述】:

所以我们这里有这个方法,系统的其余部分可以根据输入调用底层方法。

public SomeReturnObj doSomethingWithInputs(List<Input> inputs) {
  for(Input input : inputs) {
    if(input.getName().equals("A") {
        handleAInput(input);
    }
    else if(input.getName().equals("B") {
        handleBInput(input);
    }
    else { ... }
  }

  // ...
}

为了获得良好的代码覆盖率,我想测试一下,如果我将两个 Inputs 名称为 A 和三个名称为 B 的列表放在一起,则相应的内部方法会被调用两次或三次,分别。

所以我尝试了以下方法:

@Test
public void separatingInputsByName() {
    Input entry1 = mock(Input .class);
    Input entry2 = mock(Input .class);
    Input entry3 = mock(Input .class);

    doReturn("A").when(entry1).getName();
    doReturn("A").when(entry2).getName();
    doReturn("B").when(entry3).getName();

    ClassUnderTest sut = mock(ClassUnderTest .class);

    sut.doSomethingWithInputs(Arrays.asList(entry1, entry2, entry3));

    verify(sut).handleAInput(entry1);
    verify(sut).handleAInput(entry2);
    verify(sut).handleBInput(entry3);
}

很遗憾,这并没有导致正确调用内部方法,可能是因为被测类是模拟的,所以方法实现不同。

如何正确测试/验证这样的方法?

【问题讨论】:

    标签: java unit-testing junit mocking mockito


    【解决方案1】:

    你应该使用spy(),而不是mock()

    当您使用mock() 时,所有方法都被“覆盖”,以便采取默认操作而不是调用真实方法; spy() 只会注册方法调用。

    因此:

    ClassUnderTest sut = spy(new ClassUnderTest(...));
    
    sut.doSomethingWithInputs(Arrays.asList(entry1, entry2, entry3));
    
    verify(sut).handleAInput(entry1);
    verify(sut).handleAInput(entry2);
    verify(sut).handleBInput(entry3);
    verifyNoMoreInteractions(sut); // if necessary
    

    此外,您还可以:

    when(entry1.getName()).thenReturn("A");
    

    就我个人而言,我觉得它更容易阅读,但这当然是个人喜好问题。

    另外,您可以在您的情况下使用InOrder

    final InOrder inOrder = inOrder(sut);
    
    inOrder.verify(sut).handleAInput(entry1);
    // etc
    

    【讨论】:

    • 谢谢,这真的很有帮助。在这种情况下,我真的不需要inOrder,但将来可能会有用。我已将这两种方法都添加到 return null 以防止在构建 SUT 时产生副作用。这是一种有效的方法吗?
    • 不确定您在这里指的是什么副作用?
    • 我在其中一个内部方法中得到了 NPE,因为内部类因为访问数据库而被模拟。当然,如果没有指定,从模拟 DAO 中获取某些东西会返回 null,并且对该对象的所有进一步操作都会导致 NPE。因此,我只想让内部方法什么都不做(在这个测试用例中),我只想确保它们被调用。
    • 嗯,您可以将when 与间谍结合使用...例如,doNothing().when(sut.handleA())
    • 啊,mockito 太棒了。非常感谢您的洞察力。
    猜你喜欢
    • 2023-03-19
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    相关资源
    最近更新 更多