Kann Mockito eine Methode ohne Rücksicht auf das Argument stubben?

302

Ich versuche, alten Code mit Mockito zu testen.

Ich möchte einen Stub FooDao, der in der Produktion verwendet wird, wie folgt verwenden:

foo = fooDao.getBar(new Bazoo());

Ich kann schreiben:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Das offensichtliche Problem ist jedoch, dass getBar()niemals mit demselben BazooObjekt aufgerufen wird, für das ich die Methode abgestubbt habe. (Verfluche diesen newOperator!)

Ich würde es lieben, wenn ich die Methode so stubben könnte, dass sie myFoounabhängig vom Argument zurückgegeben wird. Andernfalls höre ich mir andere Vorschläge zur Problemumgehung an, möchte aber wirklich vermeiden, den Produktionscode zu ändern, bis eine angemessene Testabdeckung vorliegt.

Eric Wilson
quelle

Antworten:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

oder (um nulls zu vermeiden ):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

Vergessen Sie nicht, Matcher zu importieren (viele andere sind verfügbar):

Für Mockito 2.1.0 und neuer:

import static org.mockito.ArgumentMatchers.*;

Für ältere Versionen:

import static org.mockito.Matchers.*;
Tomasz Nurkiewicz
quelle
2
Ich liebe es, wenn die Antwort vor dem Ende des Einfrierens der Antwortannahme steht.
Eric Wilson
10
Es gibt eine notNull(Bazoo.class)genau wie die any(Bazoo.class)(vielleicht gab es sie zum Zeitpunkt dieser Antwort nicht)
Dandre Allison
2
Ich hatte eine etwas besondere Situation, in der ich eines von zwei möglichen Argumenten haben konnte - Bazoooder Cazoodie beide Unterklassen von beispielsweise sind Azoo. denn Bazooich musste zurückkehren foo, aber Cazooich musste zurückkehren bar. In dieser Situation Matchers.any()funktioniert die vorgeschlagene Lösung nicht, funktioniert jedoch Matchers.isA()perfekt.
Tanvir
3
org.mockito.Matchersist jetzt veraltet - verwenden Sie org.mockito.ArgumentMatchersstattdessen, dh import static org.mockito.ArgumentMatchers.*(siehe Dokumente )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6rchid
18

Verwenden Sie wie folgt:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Bevor Sie importieren müssen Mockito.Matchers

Hamad
quelle
1
Das ist beraubt!
DrB
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() sollte Ihren Bedürfnissen entsprechen.

Sie können auch immer in Betracht ziehen, hashCode()und equals()für die BazooKlasse zu implementieren . Dadurch funktioniert Ihr Codebeispiel wie gewünscht.

Buhb
quelle
Ich stimme dem zweiten Vorschlag zu, aber ich entscheide mich immer noch, dies aus nicht technischen Gründen nicht zu tun.
Eric Wilson
1
Die Matchers-Klasse ist veraltet (siehe Dokumentation - "Diese Klasse wird wahrscheinlich in Version 3.0 entfernt" )
Johannes Rabauer
1

Eine andere Möglichkeit ist, sich auf eine gute altmodische equalsMethode zu verlassen. Solange das Argument im whenMock mit equalsdem Argument im zu testenden Code übereinstimmt, stimmt Mockito mit dem Mock überein.

Hier ist ein Beispiel.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

Wenn Sie dann wissen, wofür der Wert sein someFieldwird, können Sie ihn so verspotten.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

Profis: Dies ist expliziter als anyMatcher. Als Prüfer von Code halte ich ein Auge offen für anyden Code, den Junior-Entwickler schreiben, da er einen Blick auf die Logik ihres Codes wirft, um das entsprechende Objekt zu generieren, das übergeben wird.

con: Manchmal ist das Feld, das an das Objekt übergeben wird, eine zufällige ID. In diesem Fall können Sie das erwartete Argumentobjekt nicht einfach in Ihrem Scheincode erstellen.

Ein anderer möglicher Ansatz ist die Verwendung von Mockitos AnswerObjekt, das mit der whenMethode verwendet werden kann. AnswerMit dieser Option können Sie den tatsächlichen Aufruf abfangen, das Eingabeargument überprüfen und ein Scheinobjekt zurückgeben. Im folgenden Beispiel anyfange ich jede Anfrage an die verspottete Methode ab. Aber dann Answerkann ich im Lambda das Bazo-Argument weiter untersuchen ... vielleicht um zu überprüfen, ob ihm ein korrekter Ausweis übergeben wurde. Ich ziehe dies anyallein vor, damit zumindest eine gewisse Prüfung des Arguments durchgeführt wird.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Um es zusammenzufassen, ich verlasse mich gerne darauf equals(wo das erwartete Argument und das tatsächliche Argument gleich sein sollten) und wenn Gleichheit nicht möglich ist (weil ich den Zustand des tatsächlichen Arguments nicht vorhersagen kann), greife ich zurück auf , Answerum das Argument zu untersuchen.

Jose Martinez
quelle