我可以在实例化时设置一个带有期望(验证)的 Mockito 模拟吗?
Can I set up a Mockito mock with expectations (verify) at instantiation time?
在 Mockito 中验证预期方法是否具有 运行 通常是这样的:
when(mockFoo.someMethod()).thenReturn(someValue);
// run test
verify(mockFoo, times(n)).someMethod();
有什么方法可以让我在创建模拟时指定验证。在类似 EasyMock 的东西中我可以做:
mockFoo = EasyMock.createMock(Foo.class);
EasyMock.expect(mockFoo.someMethod()).times(n).andReturn(someValue);
// then run test
我的用例是我有一个经常重用的测试依赖项,我想模拟它(doesFooMethodAndReturnBar5Times 模拟),但是使用 Mockito 我没有办法对其执行验证。
更新:这个答案发生了重大变化,因为 Mockito 2 中提供了严格的模拟,而 Mockito 3 中默认情况下将提供对严格存根的强制执行。使用 strict
和 lenient
模式来配置这些模拟和存根并查看 mockito issue 769 文档和进度。
在 Mockito 2 之前,这不是 Mockito 可以轻易做到的事情。 EasyMock 的默认严格模拟确保 (1) 意外交互立即失败,以及 (2) 所有预期交互发生;除了 verifyNoMoreInteractions
(它不会立即失败,而是在测试结束时失败)之外,Mockito 没有任何设置。这是 philosophical design decision on Mockito's part, and see this thread 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 模拟被命名为 "nice mocks"; Mockito 的一大卖点是模拟默认情况下很好,因此它们通常可以容忍与被测试行为无关的调用。这确实使调试变得有点困难,因为 Mockito 不会像 EasyMock 那样在意外交互时立即失败,但它也使测试不那么脆弱——更有可能的是安全更改仍然会破坏测试,因为 EasyMock 模拟接到了一个意想不到的电话。 在继续之前,请与您的团队核实他们是否会对您在此处的选择感到满意:对于 Mockito 和损坏的,严格的模拟语义相对较新假设可能会有与框架变化一样大的交易。 (至此,在看到替代方案之后,他们可能会让您最终使用 EasyMock!)
要在 Mockito 2+ 中使用严格模拟, 请参阅 syntax and libraries in Mockito issue 769。这可能是从 Mockito 1.x.
升级的一个很好的理由
要在 Mockito 中模拟严格的模拟 1.x,您需要设置一个未通过测试的默认答案,并且仅使用 doVerb
方法(doAnswer
、doReturn
、doThrow
等)。此语法为 Mockito 提供了停用存根行为所需的警告。要创建默认答案,您可以为单个方法(首选)或为单个模拟上的所有方法设置行为。
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 将始终在最后定义的匹配链上进行 return 行为,并且仅在没有链匹配时才使用默认答案,因此您应该能够使用 [=16= 定义任意数量的链] 方法来覆盖该行为。
在 Mockito 中验证预期方法是否具有 运行 通常是这样的:
when(mockFoo.someMethod()).thenReturn(someValue);
// run test
verify(mockFoo, times(n)).someMethod();
有什么方法可以让我在创建模拟时指定验证。在类似 EasyMock 的东西中我可以做:
mockFoo = EasyMock.createMock(Foo.class);
EasyMock.expect(mockFoo.someMethod()).times(n).andReturn(someValue);
// then run test
我的用例是我有一个经常重用的测试依赖项,我想模拟它(doesFooMethodAndReturnBar5Times 模拟),但是使用 Mockito 我没有办法对其执行验证。
更新:这个答案发生了重大变化,因为 Mockito 2 中提供了严格的模拟,而 Mockito 3 中默认情况下将提供对严格存根的强制执行。使用 strict
和 lenient
模式来配置这些模拟和存根并查看 mockito issue 769 文档和进度。
在 Mockito 2 之前,这不是 Mockito 可以轻易做到的事情。 EasyMock 的默认严格模拟确保 (1) 意外交互立即失败,以及 (2) 所有预期交互发生;除了 verifyNoMoreInteractions
(它不会立即失败,而是在测试结束时失败)之外,Mockito 没有任何设置。这是 philosophical design decision on Mockito's part, and see this thread 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 模拟被命名为 "nice mocks"; Mockito 的一大卖点是模拟默认情况下很好,因此它们通常可以容忍与被测试行为无关的调用。这确实使调试变得有点困难,因为 Mockito 不会像 EasyMock 那样在意外交互时立即失败,但它也使测试不那么脆弱——更有可能的是安全更改仍然会破坏测试,因为 EasyMock 模拟接到了一个意想不到的电话。 在继续之前,请与您的团队核实他们是否会对您在此处的选择感到满意:对于 Mockito 和损坏的,严格的模拟语义相对较新假设可能会有与框架变化一样大的交易。 (至此,在看到替代方案之后,他们可能会让您最终使用 EasyMock!)
要在 Mockito 2+ 中使用严格模拟, 请参阅 syntax and libraries in Mockito issue 769。这可能是从 Mockito 1.x.
升级的一个很好的理由要在 Mockito 中模拟严格的模拟 1.x,您需要设置一个未通过测试的默认答案,并且仅使用 doVerb
方法(doAnswer
、doReturn
、doThrow
等)。此语法为 Mockito 提供了停用存根行为所需的警告。要创建默认答案,您可以为单个方法(首选)或为单个模拟上的所有方法设置行为。
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 将始终在最后定义的匹配链上进行 return 行为,并且仅在没有链匹配时才使用默认答案,因此您应该能够使用 [=16= 定义任意数量的链] 方法来覆盖该行为。