2013-01-17 11 views
7

mit dem folgenden Code:Mockito - Mocking Betonklassen

LinkedList list = mock(LinkedList.class); 
    doCallRealMethod().when(list).clear(); 
    list.clear(); 

durch diesen Test ausführen, eine Nullpointer von der ersten Zeile in LinkedList # klar geworfen wird:

public void clear() { 
    Entry<E> e = header.next; 
    while (e != header) { 
     Entry<E> next = e.next; 
     //Code omitted. 

aber Header wurde instanziiert vor:

private transient Entry<E> header = new Entry<E>(null, null, null); 

Könnte etwas Bitte erklären Sie, was während der Mock-Erstellung passiert.

####### UPDATE. ######

Nachdem ich alle Antworten gelesen habe, habe ich in Objesisis Quellcode geschaut und herausgefunden, dass Reflection API verwendet wird, um die Proxy Instanz (über CGLIB) zu erstellen und somit alle Konstruktoren in der Hierarchie zu umgehen bis java.lang.Object.

Hier ist der Beispielcode um das Problem zu simulieren:

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

zeigen Sie uns full stacktrace – Archer

Antwort

5

Ihre Argumentation ist einwandfrei.
Das Hauptproblem ist, dass Sie nicht auf dem tatsächlichen LinkedList Objekt arbeiten. Hier ist, was passiert hinter den Kulissen:

Das Objekt, das Sie von Mockito mock() erhalten, ist ein Enhancer Objekt aus der CGLIB-Bibliothek.

Für mich ist es so etwas wie java.util.LinkedList$$EnhancerByMockitoWithCGLIB$$cae81a28

welche Art von wirkt wie ein Proxy, wenn auch mit den auf Standardwerte gesetzt Felder aus. (null, 0 usw.)

+0

Sie haben Recht, aber die Ausnahme wird vom realen Objekt geworfen. selbst beim debuggen kann ich zur echten methode greifen. Meine Frage ist, ob es hinter der Szene ein echtes "LinkedList" -Objekt gibt, dann sollte auch der interne Zustand in Ordnung sein. – mhshams

+0

Der Grund, warum Sie auf das Mocked-Objekt mit einer LinkedList-Referenz verweisen können, bedeutet, dass das Mocked-Objekt eine Unterklasse von LinkedList ist. Ich hoffe, das hilft. –

+0

Auch wenn das Mock-Objekt eine Unterklasse von Linkedlist ist, müssen Sie zum Erstellen dieses Unterklassenobjekts ein Elternklassenobjekt erstellen. (Einer der Elternkonstruktoren hätte aufgerufen werden sollen) – mhshams

7

Sie nur Mockito fragen die reale Sache auf klar zu nennen, ist das zugrunde liegende Objekt noch eine Fälschung von Mockito für Sie erstellt. Wenn Sie eine echte LinkedList benötigen, dann verwenden Sie einfach die LinkedList - nur der am meisten geheizte Purist von BDD würde Ihnen sagen, dass Sie alles um Sie herum verspotten sollten. Ich meine, du machst dich nicht lustig Strings du bist?

Mockito Autor selbst hat gesagt, dass die Berufung der realen Sache sollte kaum verwendet werden, in der Regel nur zum Testen eines Legacy-Code.

Wenn Sie auf dem realen Objekt auszuspionieren müssen (die Anrufungen verfolgen), dann hat Mockito eine Funktion für diese zu:

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

Mit Spion, können Sie immer noch ein Verfahren Stummel, wenn Sie benötigen. Es funktioniert grundsätzlich wie ein Schein, aber ist nicht;)

+1

Das stimmt, aber beantwortet die Frage nicht. Außerhalb der Tatsache, dass es eine nicht so gute Idee ist, dies zu tun, können wir trotzdem herausfinden, warum die Ausnahme passiert, nicht wahr? Vlad gibt die Erklärung, +1 für ihn. – Fildor

+0

Wie ich traurig bin: Du fragst Mockito nur, dass das reale Ding auf Clear gesetzt wird, das Objekt selbst wird nicht instanziiert, Mockito erzeugt darunter eine Fälschung für dich. Ich habe die Antwort überarbeitet, um sie klarer zu machen. Ich werde versuchen, das nächste Mal expliziter zu sein. – theadam

+1

Nichts für ungut :) Ich wollte nur darauf hinweisen, dass es manchmal Antworten gibt, die nicht die Frage beantworten, sondern die Vorgehensweise des OP in Frage stellen. Und diese werden manchmal dafür abgelehnt. Ich fand es eine gute Übung in der Antwort, dem OP zuerst zu sagen, warum es nicht funktioniert, was er tut (und damit die Frage zu beantworten) und dann eine Alternative vorzuschlagen, wenn ich eine anbieten muss. Nach Ihrer Bearbeitung, +1 für Sie auch. – Fildor

1

Wenn Sie eine Klasse verspotten, ist das Objekt, das Sie verwenden, eine Fälschung, deshalb werden die Variablen nicht instanziiert und die Methoden funktionieren nicht wie erwartet. Sie können Reflection verwenden, um einen Wert für den Header festzulegen, aber ich würde das wirklich nicht empfehlen. Wie der Adam sagte, wäre es das Beste, nur eine Liste zu verwenden.