我在JUnit测试中使用Mockito。该测试是一个集成测试,测试整个场景,因此其中有许多断言和verify(mock)
我的问题是,我正在编写一些生产代码,做一些断言,然后验证mock是否有效,直到出现相同的mock调用。请参阅简化代码:
interface I {
void m1();
void m2();
}
// some decorator
class T implements I {
public T(I decorated) {
/*...*/
}
/* ... */
}
public void testInvalid() {
I m = mock(I.class);
T t = new T(m);
t.m1();
assertEquals("m1", t.getLastMethod());
verify(m).m1();
t.m2();
assertEquals("m2", t.getLastMethod());
verify(m).m2();
t.m1();
assertEquals("m1", t.getLastMethod());
verify(m).m1();
// TooManyActualInvocations t.m1(): Wanted 1 time, but was 2 times ...
}
public void testValid() {
I m = mock(I.class);
T t = new T(m);
t.m1();
assertEquals("m1", t.getLastMethod());
t.m2();
assertEquals("m2", t.getLastMethod());
t.m1();
assertEquals("m1", t.getLastMethod());
verify(m, times(2)).m1();
verify(m).m2();
}
一个想法是在最后验证mock,但假设有一个愚蠢的实现错误,导致调用方法m1两次,调用m2一次,但并不像我在testInvalid
中预期的那样,但最终测试会通过。我希望我的测试早点失败。我该如何做到这一点?
谢谢。
感谢@Woozy Coder:
没有提到,reset
也是一个选项,但由于它必须在验证和下一个相等的存根调用之间调用,我认为这会使编写"漂亮"和正确的测试变得困难。应该有两种不同的嘲讽风格:
- "后条件"嘲笑Mockito
- "早期"嘲讽,这将是验证块后的隐式重置
类似于:
earlyReset(m).after(
new Runnable() {
t.someMethodInvokingTwoStubs();
verify(m).someMethod1();
verify(m).someMethod2();
}
);
我遇到了类似的问题,决定使用clearInvocations()
(Mockito 2.1中提供)
https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/Mockito.html#clearInvocations-T…-
使用reset()
有一个缺点,那就是您也松了存根,clearInvocations()
只清除调用。
Mockito是为了避免脆性而编写的,因此验证可以使最不具体的断言成为可能,从而允许实现在不更改测试的情况下进行发展如果你多次调用这些方法对你的测试系统来说无关紧要,那么你不应该让Mockito检查它。
备选方案:
- 使用
atLeast
或atLeastOnce
确保调用发生,而不必担心该方法被调用了多少次 - 如果调用被存根化为具有返回值,则可以根据关于已存根化的数据的状态断言推断系统工作
- 如果您真的需要在单个测试方法中更改存根或验证行为,那么您的mock可能会超出Mockito所擅长的范围。对单个方法使用Answer,或者编写一个手动Fake来正确模拟互连的方法