我可以在实例化时设置一个带有期望(验证)的Mockito mock吗



验证预期的方法是否已在Mockito中运行通常如下所示:

when(mockFoo.someMethod()).thenReturn(someValue);
// run test
verify(mockFoo, times(n)).someMethod();

在创建mock时,有什么方法可以指定验证吗。在像EasyMock这样的东西中,我可以做:

mockFoo = EasyMock.createMock(Foo.class);
EasyMock.expect(mockFoo.someMethod()).times(n).andReturn(someValue);
// then run test

我的用例是,我有一个经常重复使用的测试依赖项,我想模拟它(doesFooMethodAndReturnBar5Times模拟),但使用Mockito,我没有办法对其进行验证。

更新:这个答案发生了重大变化,因为Mockito 2中提供了严格的mock,而Mockito 3中默认情况下将提供对严格存根的强制执行。使用strictlenient模式来配置这些mock和stub,并查看mockito问题769中的文档和进度。

在Mockito 2之前,这不是Mockito可以轻易做到的。EasyMock默认的严格模拟确保(1)意外的交互立即失败,(2)所有预期的交互都发生;除了verifyNoMoreInteractions(它不会立即失败,而是在测试结束时失败)之外,Mockito并没有任何设置。这是Mockito的一个哲学设计决定,请参阅Mockito创始人进一步讨论的这条线索。


事实上,Mockito的when语法取决于它是否允许意外交互,因为意外交互告诉Mockito调用了哪种方法:

when(mockFoo.someMethod()).thenReturn(someValue);
//   ^^^^^^^^^^^^^^^^^^^^  Java calls this first to get an argument for when,
//                         which is how Mockito knows which method to stub:
//                         it's always the last one called.

EasyMock能够容忍意外调用的mock被命名为"nice mocks";Mockito的一大卖点是,默认情况下mock很好,因此它们通常可以容忍与测试行为无关的调用。这确实让调试变得有点困难,因为Mockito不会像EasyMock那样在意外的交互中立即失败,但它也降低了测试的脆弱性——更可能的是,安全的更改仍然会破坏测试,因为EasyMock-mock收到了意外的调用在继续之前,请与您的团队核实他们是否对您的选择感到满意:对于Mockito来说,严格的mock语义是相对较新的,而破碎的假设可能与框架更改一样重要。(到那时,在看到替代方案后,他们可能会让你使用EasyMock!)


要在Mockito 2+中使用严格的mocks,请参阅Mockito 769期中的语法和库。这可能是从Mockito 1.x升级的一个很好的理由

要在Mockito 1.x中模拟严格的mock,您需要设置一个未通过测试的默认Answer,并且只使用doVerb方法(doAnswerdoReturndoThrow等)来建立正确的行为。此语法为Mockito提供了停用存根行为所需的警告。要创建默认的Answer,可以为单个方法(首选)设置行为,也可以为单个mock上的所有方法设置行为。

public class ThrowingAnswer extends Answer<Object> {
  @Override public Object answer(InvocationOnMock invocation) {
    throw new AssertionError("Unexpected invocation: " + invocation);
  }
}
// apply to the entire object:
YourObject yourObject = Mockito.mock(YourObject.class, new ThrowingAnswer());
// or per-method:
YourObject yourObject = Mockito.mock(YourObject.class);
doAnswer(new ThrowingAnswer()).when(yourObject).scaryMethod(any());

Mockito将始终返回最后定义的匹配链上的行为,并且只有在没有链匹配的情况下才会使用默认答案,因此您应该能够使用doVerb方法定义任意数量的链来覆盖该行为。

最新更新