繁体   English   中英

Mockito - 模拟混凝土类

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM