Unvollendete Stubbing in Mockito entdeckt

150

Beim Ausführen der Tests wird folgende Ausnahme angezeigt. Ich benutze Mockito zum Verspotten. Die von der Mockito-Bibliothek erwähnten Hinweise helfen nicht weiter.

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
    -> at com.a.b.DomainTestFactory.myTest(DomainTestFactory.java:355)

    E.g. thenReturn() may be missing.
    Examples of correct stubbing:
        when(mock.isOk()).thenReturn(true);
        when(mock.isOk()).thenThrow(exception);
        doThrow(exception).when(mock).someVoidMethod();
    Hints:
     1. missing thenReturn()
     2. you are trying to stub a final method, you naughty developer!

        at a.b.DomainTestFactory.myTest(DomainTestFactory.java:276)
        ..........

Testcode von DomainTestFactory. Wenn ich den folgenden Test durchführe, wird die Ausnahme angezeigt.

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); // Line 355
}

private List<SomeModel> getSomeList() {
    SomeModel model = Mockito.mock(SomeModel.class);
    Mockito.when(model.getName()).thenReturn("SomeName"); // Line 276
    Mockito.when(model.getAddress()).thenReturn("Address");
    return Arrays.asList(model);
}

public class SomeModel extends SomeInputModel{
    protected String address;
    protected List<SomeClass> properties;

    public SomeModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    public String getAddress() {
        return this.address;
    }

}

public class SomeInputModel{

    public NetworkInputModel() {
        this.Properties = new java.util.ArrayList<SomeClass>(); 
    }

    protected String Name;
    protected List<SomeClass> properties;

    public String getName() {
        return this.Name;
    }

    public void setName(String value) {
        this.Name = value;
    }
}
Königliche Rose
quelle
Hallo Mureinik, ich habe den Beitrag mit Zeilennummern aktualisiert
Royal Rose

Antworten:

370

Du nistest Spott in Spott. Sie rufen an getSomeList(), was einige Verspottungen macht, bevor Sie die Verspottung beendet haben MyMainModel. Mockito mag es nicht, wenn Sie dies tun.

Ersetzen

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    Mockito.when(mainModel.getList()).thenReturn(getSomeList()); --> Line 355
}

mit

@Test
public myTest(){
    MyMainModel mainModel =  Mockito.mock(MyMainModel.class);
    List<SomeModel> someModelList = getSomeList();
    Mockito.when(mainModel.getList()).thenReturn(someModelList);
}

Um zu verstehen, warum dies ein Problem verursacht, müssen Sie ein wenig über die Funktionsweise von Mockito wissen und wissen, in welcher Reihenfolge Ausdrücke und Anweisungen in Java ausgewertet werden.

Mockito kann Ihren Quellcode nicht lesen. Um herauszufinden, was Sie von ihm verlangen, hängt er stark vom statischen Status ab. Wenn Sie eine Methode für ein Scheinobjekt aufrufen, zeichnet Mockito die Details des Aufrufs in einer internen Liste von Aufrufen auf. Die whenMethode liest den letzten dieser Aufrufe aus der Liste und zeichnet diesen Aufruf in dem zurückgegebenen OngoingStubbingObjekt auf.

Die Linie

Mockito.when(mainModel.getList()).thenReturn(someModelList);

verursacht die folgenden Wechselwirkungen mit Mockito:

  • Die Scheinmethode mainModel.getList()heißt:
  • Statische Methode whenheißt,
  • Die Methode thenReturnwird für das OngoingStubbingvon der whenMethode zurückgegebene Objekt aufgerufen .

Die thenReturnMethode kann dann den über die OngoingStubbingMethode empfangenen Mock anweisen , jeden geeigneten Aufruf der zurückzugebenden getListMethode zu verarbeiten someModelList.

Da Mockito Ihren Code nicht sehen kann, können Sie Ihre Verspottung auch wie folgt schreiben:

mainModel.getList();
Mockito.when((List<SomeModel>)null).thenReturn(someModelList);

Dieser Stil ist etwas weniger klar zu lesen, zumal in diesem Fall der nullgegossen werden muss, aber er erzeugt die gleiche Abfolge von Interaktionen mit Mockito und erzielt das gleiche Ergebnis wie die obige Zeile.

Allerdings ist die Linie

Mockito.when(mainModel.getList()).thenReturn(getSomeList());

verursacht die folgenden Wechselwirkungen mit Mockito:

  1. Die Scheinmethode mainModel.getList()heißt:
  2. Statische Methode whenheißt,
  3. Ein neues mockvon SomeModelwird erstellt (innen getSomeList()),
  4. Die Scheinmethode model.getName()heißt:

An diesem Punkt wird Mockito verwirrt. Es dachte, Sie verspotten mainModel.getList(), aber jetzt sagen Sie, dass Sie die model.getName()Methode verspotten wollen . Für Mockito sieht es so aus, als würden Sie Folgendes tun:

when(mainModel.getList());
// ...
when(model.getName()).thenReturn(...);

Das sieht albern aus, Mockitoda es nicht sicher sein kann, womit Sie es machen mainModel.getList().

Beachten Sie, dass wir nicht zum thenReturnMethodenaufruf gelangt sind , da die JVM die Parameter dieser Methode auswerten muss, bevor sie die Methode aufrufen kann. In diesem Fall bedeutet dies, die getSomeList()Methode aufzurufen .

Im Allgemeinen ist es eine schlechte Entwurfsentscheidung, sich wie Mockito auf den statischen Zustand zu verlassen, da dies zu Fällen führen kann, in denen das Prinzip des geringsten Erstaunens verletzt wird. Mockitos Design sorgt jedoch für klare und ausdrucksstarke Verspottung, auch wenn es manchmal zu Erstaunen führt.

Schließlich fügen neuere Versionen von Mockito der obigen Fehlermeldung eine zusätzliche Zeile hinzu. Diese zusätzliche Zeile zeigt an, dass Sie sich möglicherweise in derselben Situation wie diese Frage befinden:

3: Sie stubben das Verhalten eines anderen Mocks vor der Anweisung 'thenReturn', wenn diese abgeschlossen ist

Luke Woodward
quelle
Gibt es eine Erklärung für diese Tatsache? Lösung funktioniert. Und ich verstehe nicht, warum die 'in-place'-Mock-Erstellung nicht funktioniert. Wenn Sie einen Mock erstellen und einen erstellten Mock unter Bezugnahme auf einen anderen Mock übergeben, funktioniert dies.
Capacytron
1
Ausgezeichnete Antwort, liebe SO! Ich hätte
ewig gebraucht
4
Großartige Antwort, Luke! Sehr detaillierte Erklärung in einfachen Worten. Danke dir.
Tomasz Kalkosiński
1
Genial. Das Lustige ist, wenn ich den direkten Methodenaufruf mache und langsam debugge, dann funktioniert es. Das Attribut von CGLIB $ BOUND erhält den Wert true, aber irgendwie dauert es ein wenig. Wenn ich den direkten Methodenaufruf verwende und vor dem Training stoppe (wenn ...), sehe ich, dass der Wert zuerst falsch ist und später wahr wird. Wenn es falsch ist und das Training beginnt, tritt diese Ausnahme auf.
Michael Hegner
Du hast meinen Tag gerettet! Dies ist die Art von Fehler, bei der Sie viel Zeit verschwenden! Ich dachte, es war etwas mit Kotlin am Anfang zu tun
Bronx
1
org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.

Versuchen Sie zum Verspotten von Void-Methoden Folgendes:

//Kotlin Syntax

 Mockito.`when`(voidMethodCall())
           .then {
                Unit //Do Nothing
            }
Takharsh
quelle