8

给定以下代码:

    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 源代码,发现它使用反射 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;
    }
}
4

3 回答 3

8

您只是要求 Mockito 将真实的东西称为 clear,底层对象仍然是 Mockito 为您创建的假货。如果您需要一个真正的 LinkedList,那么只需使用 LinkedList - 只有最狂热的 BDD 纯粹主义者会告诉您模拟周围的一切。我的意思是,你不是在嘲笑字符串吗?

Mockito 作者自己说过,调用真实的东西应该很少使用,通常只用于测试遗留代码。

如果您需要监视真实对象(跟踪调用),那么 Mockito 也有一个功能:

List list = new LinkedList();
List spy = spy(list);

使用 spy,如果需要,您仍然可以存根方法。它基本上像模拟一样工作,但不是;)

于 2013-01-17T08:10:39.577 回答
6

你的推理是完美的。
关键问题是您没有在实际LinkedList对象上进行操作。以下是幕后发生的事情:

Mockito 给您的对象是 CGLIB 库mock() 中的Enhancer对象。

对我来说,这就像java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28

哪种行为类似于代理,尽管字段设置为默认值。(空,0等)

于 2013-01-17T08:40:30.853 回答
1

当你模拟一个类时,你使用的对象是假的,因此变量没有实例化,方法也没有按预期工作。您可以使用反射为标头设置值,但我真的不推荐这样做。正如 theadam 所说,最好的办法就是使用一个列表。

于 2013-01-17T08:13:03.607 回答