[英]Mockito - Mocking Concrete Classes
给出以下代码:
LinkedList list = mock(LinkedList.class);
doCallRealMethod().when(list).clear();
list.clear();
通过执行此测试,从LinkedList#clear的第一行抛出NullPointerException:
public void clear() {
Entry<E> e = header.next;
while (e != header) {
Entry<E> next = e.next;
//Code omitted.
但是标题之前已被实例化:
private transient Entry<E> header = new Entry<E>(null, null, null);
有人可以解释模拟创作过程中发生了什么吗?
#######更新。 ######
在阅读了所有答案,特别是Ajay的答案后,我查看了Objenesis源代码并发现它使用Reflection API创建代理实例(通过CGLIB),因此绕过层次结构中的所有构造函数,直到java.lang.Object。
以下是模拟问题的示例代码:
public class ReflectionConstructorTest {
@Test
public void testAgain() {
try {
//java.lang.Object default constructor
Constructor javaLangObjectConstructor = Object.class
.getConstructor((Class[]) null);
Constructor mungedConstructor = ReflectionFactory
.getReflectionFactory()
.newConstructorForSerialization(CustomClient.class, javaLangObjectConstructor);
mungedConstructor.setAccessible(true);
//Creates new client instance without calling its constructor
//Thus "name" is not initialized.
Object client = mungedConstructor.newInstance((Object[]) null);
//this will print "CustomClient"
System.out.println(client.getClass());
//this will print "CustomClient: null". name is null.
System.out.println(client.toString());
} catch(Exception e) {
e.printStackTrace();
}
}
}
class CustomClient {
private String name;
CustomClient() {
System.out.println(this.getClass().getSimpleName() + " - Constructor");
this.name = "My Name";
}
@Override
public String toString() {
return this.getClass().getSimpleName() + ": " + name;
}
}
你只是要求Mockito清楚地打电话给真实的东西,底层物品仍然是Mockito为你创造的假货。 如果你需要一个真正的LinkedList,那么只需使用LinkedList - 只有BDD最热门的纯粹主义者会告诉你模仿你周围的一切。 我的意思是,你不是在嘲笑字符串是你吗?
Mockito作者自己曾说过,调用真实的东西几乎不应该使用,通常只用于测试遗留代码。
如果您需要监视真实对象(跟踪调用),那么Mockito也有这样的功能:
List list = new LinkedList();
List spy = spy(list);
使用间谍,如果需要,您仍然可以存根方法。 它基本上像模拟,但不是;)
你的推理完美无瑕。
关键问题是您没有对实际的LinkedList
对象进行操作。 以下是幕后发生的事情:
Mockito的mock()
给出的对象是CGLIB库中的Enhancer对象。
对我来说,它类似于java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28
哪种行为类似于代理,尽管字段设置为默认值。 (null,0等)
当您模拟一个类时,您使用的对象是假的,因此变量不会被实例化,并且方法无法按预期工作。 您可以使用反射来设置标题的值,但我真的不建议这样做。 正如theadam所说,最好的办法就是只使用一个清单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.