2013-01-17 65 views
7

鉴于以下代码:的Mockito - 惩戒具体类

LinkedList list = mock(LinkedList.class); 
    doCallRealMethod().when(list).clear(); 
    list.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; 
    } 
} 
+0

我们展示了全堆栈跟踪 – Archer

回答

5

你的推理是完美无瑕的。
关键的问题是,你是不是实际LinkedList对象上运行。以下是幕后发生的事情:

Mockito的mock()给出的对象是来自CGLIB库的Enhancer对象。

对我来说是一样的东西java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28

这有点像代理行为,尽管有设置为默认值的字段。 (NULL,0等)

+0

你是对的,但是这个例外是从真实对象中抛出的。即使通过调试我可以达到真正的方法。我的问题是,如果场景背后有一个真正的“LinkedList”对象,那么它的内部状态应该也可以。 – mhshams

+0

您能够使用LinkedList引用来引用模拟对象的原因意味着模拟对象是LinkedList的子类。希望有所帮助。 –

+0

即使模拟对象是Linkedlist的子类,为了创建这个子类对象,您需要创建父类对象。 (应该调用父构造函数) – mhshams

7

你只是要求给的Mockito呼吁清晰真实的东西,根本目的仍然是为的Mockito您创建一个假的。如果你需要一个真正的LinkedList,那么就使用LinkedList - 只有BDD最激烈的纯粹主义者会告诉你嘲笑你周围的一切。我的意思是,你不是在嘲笑你是弦吗?

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

如果你需要刺探真实物体上(跟踪调用),则具有的Mockito这个功能太:

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

随着间谍,你仍然可以存根的方法,如果你需要。当你嘲笑一个类,你正在使用的对象是假的,所以变量没有被实例化,并如期方法不起作用)

+1

这是事实,但不回答这个问题。事实上,这样做不是一个好主意,但我们仍然可以找出异常发生的原因,不是吗?弗拉德给出了他的解释,+1。 – Fildor

+0

因为我很伤心:你只要求Mockito清楚地调用真实的东西,对象本身没有实例化,mockito在它下面为你创建一个假的。我已经修改了答案,使其更加清晰。下一次我会尝试更加明确。 – theadam

+1

没有犯法:)只是想指出,有时,有答案不回答问题,但质疑OP的行动过程。而这些有时会因为这样做而被低估。我发现这是一个很好的做法,首先告诉OP为什么它不在做他所做的事情(从而回答这个问题),然后提出一个替代方案,如果我有一个提供方案。编辑完成后,您也可以+1。 – Fildor

1

;基本上,它就像一个模拟的,但并非如此。你可以使用反射设置标题的值,但我真的不会推荐这个。正如阿达姆所说,最好的做法是仅仅使用一个列表。