我最近看到一些像这样工作的Mockito 1.9.5代码:
MyObject myObject = new MyObject();
...
Mockito.when(myObject.someMethod()).thenReturn("bogus");
由于myObject 不是一个模拟对象,但是是非模拟类的一个实例,我很惊讶这个编译并运行而不会失败单元测试。我希望我会失败说“你让我对非模拟对象设置期望,我希望只对模拟对象设置期望。”
为什么此代码不会导致测试失败?
更新:添加更多代码以实际复制我发现令人困惑的行为。这些例子充分说明了我的问题。以下代码的行为与我预期的一样 - 当我运行此测试时,测试失败并显示消息
when()需要一个必须是'模拟方法调用'的参数。
public class AnotherObject{
public String doSomething(){
return "did something";
};
}
public class MyObject{
private AnotherObject anotherObject = new AnotherObject();
public void setAnotherObject(AnotherObject anotherObject) {
this.anotherObject = anotherObject;
}
public String someMethod(){
return anotherObject.doSomething();
}
}
@Test
public void WhyDoesWhenWorkOnNonMock() throws Exception {
MyObject myObject = new MyObject();
Mockito.when(myObject.someMethod()).thenReturn("bogus");
}
现在,如果我为这个人为的测试添加一些特定的行,测试不再失败,即使我预期会出现与以前相同的失败和相同的消息:
public class AnotherObject{
public String doSomething(){
return "did something";
};
}
public class MyObject{
private AnotherObject anotherObject = new AnotherObject();
public void setAnotherObject(AnotherObject anotherObject) {
this.anotherObject = anotherObject;
}
public String someMethod(){
return anotherObject.doSomething();
}
}
@Test
public void WhyDoesWhenWorkOnNonMock() throws Exception {
MyObject myObject = new MyObject();
AnotherObject mockAnotherObject = Mockito.mock(AnotherObject.class);
myObject.setAnotherObject(mockAnotherObject);
Mockito.when(myObject.someMethod()).thenReturn("bogus");
}
答案 0 :(得分:11)
可能,令人难以置信和脆弱的巧合,除非myObject
实际上被设定为间谍。
Mockito允许创建一个"间谍"一个真实的对象:
MyObject myObject = spy(new MyObject());
Mockito.when(myObject.someMethod()).thenReturn("something");
// myObject is actually a duplicate of myObject, where all the fields are copied
// and the methods overridden. By default, Mockito silently records interactions.
myObject.foo(1);
verify(myObject).foo(anyInt());
// You can stub in a similar way, though doReturn is preferred over thenReturn
// to avoid calling the actual method in question.
doReturn(42).when(myObject).bar();
assertEquals(42, myObject.bar());
除此之外,这段代码可能不像它应该的那样工作。 when
的参数没有意义,并且糖意味着隐藏模拟的交互是对模拟的最新方法调用。例如:
SomeObject thisIsAMock = mock(SomeObject.class);
OtherObject notAMock = new OtherObject();
thisIsAMock.methodOne();
Mockito.when(notAMock.someOtherMethod()).thenReturn("bar");
// Because notAMock isn't a mock, Mockito can't see it, so the stubbed interaction
// is the call to methodOne above. Now methodOne will try to return "bar",
// even if it isn't supposed to return a String at all!
这样的不匹配可能是ClassCastException,InvalidUseOfMatchersException和其他奇怪错误的简单来源。您的真实MyObject类也可能将模拟作为参数,最后一次交互是使用someMethod
与之交互的模拟。
您的编辑确认了我的怀疑。就Java而言,它需要在调用when
之前将参数评估为when
,因此您的测试调用someMethod
(实际)。 Mockito无法看到 - 它只能在您与其中一个模拟进行交互时才采取行动 - 所以在第一个示例中,它看到与模拟的零交互,因此它失败了。在您的第二个示例中,您的someMethod
调用doSomething
,Mockito 可以看到,因此它返回默认值(null)并将其标记为最近的方法调用。然后调用when(null)
,Mockito忽略参数(null)并引用最近调用的方法(doSomething
),以及返回" bogus"从那时起。
您可以通过将此断言添加到测试中来看到,即使您从未明确地将其断言:
assertEquals("bogus", mockAnotherObject.doSomething());
作为补充参考,我写了a separate SO answer on Mockito matchers,其实现细节可能很有用。有关类似问题的扩展视图,请参阅步骤5和6。