验证使用Mockito.inOrder

时间:2018-08-05 06:23:04

标签: java unit-testing mocking mockito

我正尝试测试是否按预期顺序调用了模拟对象。下面是简化的示例:

@Test
public void test() {
    List<String> mockedList = Mockito.mock(List.class);

    for (int i = 0; i < 5; i++) {
        mockedList.add("a");
        mockedList.add("b");
        mockedList.add("c");
    }

    // I want only this to pass.
    InOrder inOrder1 = Mockito.inOrder(mockedList);
    inOrder1.verify(mockedList).add("a");
    inOrder1.verify(mockedList).add("b");
    inOrder1.verify(mockedList).add("c");

    // I want this to fail.
    InOrder inOrder2 = Mockito.inOrder(mockedList);
    inOrder2.verify(mockedList).add("c");
    inOrder2.verify(mockedList).add("b");
    inOrder2.verify(mockedList).add("a");
}

尽管验证顺序(c -> b -> a)与调用顺序(a -> b -> c)不同,但是此测试通过了。这是因为Mockito会验证method2是否在之后的任何地方 method1调用,而不是立即调用(即,之间没有其他调用方法)。当我多次添加元素时,这很有可能。这意味着Mockito InOrder通过b -> a -> c -> a -> c -> b -> c -> b -> a ...

但是我希望此操作失败,并确保订单始终为a -> b -> c -> a -> b -> c -> a -> b -> c ...

更新:正确的验证方法是验证订单的迭代次数是否相同(可接受的答案摘要)

for (int i = 0; i < 5; i++) {
    inOrder1.verify(mockedList).add("a");
    inOrder1.verify(mockedList).add("b");
    inOrder1.verify(mockedList).add("c");
}

// fail the test if we missed to verify any other invocations
inOrder1.verifyNoMoreInteractions();

2 个答案:

答案 0 :(得分:2)

问题是您需要添加

inOrder.verifyNoMoreInteractions();

通过循环,您会生成类似

的呼叫
  • 添加(a)
  • 添加(b)
  • 添加(c)
  • 添加(a)
  • 添加(b)
  • 添加(c)

随后检查

inOrder.verify(mockedList).add("b");
inOrder.verify(mockedList).add("c");
inOrder.verify(mockedList).add("a");

它与调用(add(b),add(c),add(a))匹配。其他呼叫未选中。

  • 添加(a)
  • 添加(b)
  • 添加(c)
  • 添加(a)
  • 添加(b)
  • 添加(c)

所以我认为您必须选择: 1)验证所有呼叫a,b,c,a,b,c 2)确认您的模拟游戏不再发生互动

顺便说一句,如果您将验证更改为

inOrder.verify(mockedList).add("c");
inOrder.verify(mockedList).add("b");
inOrder.verify(mockedList).add("a");

它将失败,因为它与呼叫不匹配:-)

答案 1 :(得分:0)

这里没有答案:您走错了路(至少对于给定的示例而言):

含义:当创建“ API”时,您想要实现“易于使用,难以滥用”。需要按一定顺序调用方法的API不能实现这一点。因此:感觉需要以编程方式检查订单可能表明您在做“错误的事情”。您应该设计一个“做正确的事”的API,而不是期望代码的用户为您做到这一点。

除此之外:在测试列表时,您绝对首先要使用模拟。

您要确保元素以特定顺序添加到列表中吗?然后一个简单的

assertThat(actualList, is(expectedList));

这是您的测试应检查的唯一内容!

含义:您无需检查实现细节(add()以此参数的顺序来调用),您只需检查该操作的可观察结果即可。您不在乎按什么顺序添加事物,也许不在乎重新设置和更新,您只在乎最终结果!

OP给出的评论:当您必须“按顺序”处理某些调用/对象时,则应设计一个界面,以使您传达意图。您只是通过单元测试来测试。当然,这是一个好的开始,但还不够!

基本上,有两个概念可能对您有用:

  • 序列号:当对象按顺序进入且顺序很重要时,则每个对象应收到一个唯一的(理想情况下是递增的)序列号。然后,处理元素的每个步骤都可以简单地记住上一个已处理的序列号,如果有一个 lower 出现,则会引发异常。
  • “命令”的序列。 OP希望确保方法调用依次发生。那根本不是有用的抽象。取而代之的是:可以创建一个Command类(执行“某事”),然后为每个必需的活动创建不同的子类。然后,您的处理器只需创建一个List<Command>。现在 testing 可以归结为:生成这样的序列,并检查每个条目是否具有给定的类型。