Mockito: Stubbing-Methoden, die den Typ mit begrenzten Wildcards zurückgeben

134

Betrachten Sie diesen Code:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

Der Compiler beschwert sich über die Zeile, für die versucht wird, das Verhalten zu stoppen dummyMethod(). Gibt es Hinweise darauf, wie man Stubbing-Methoden durchführt, die einen Typ mit begrenzten Platzhaltern zurückgeben?

Shikhar Mishra
quelle
Können Sie Ihr Code-Snippet aktualisieren, um die generischen Typen anzuzeigen?
Mühlenhaus
1
Getan. Ich musste Pre- und Code-Tags entfernen, sie wurden entfernt <? erweitert Number> aus der Typdeklaration.
Shikhar Mishra

Antworten:

189

Zu diesem Zweck können Sie auch die nicht typsichere Methode doReturn verwenden.

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

wie in Mockitos Google-Gruppe besprochen .

Dies ist zwar einfacher als thenAnswer, aber beachten Sie erneut, dass es nicht typsicher ist. Wenn Sie sich Sorgen um die Typensicherheit machen, ist die Antwort von millhouse richtig.

Weitere Details

Um klar zu sein, hier ist der beobachtete Compilerfehler:

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

Ich glaube, der Compiler hat während des whenAufrufs den ersten Platzhaltertyp zugewiesen und kann dann nicht bestätigen, dass der zweite Platzhaltertyp im thenReturnAufruf derselbe ist.

Es sieht so aus, als würde thenAnswerdieses Problem nicht auftreten, da ein Platzhaltertyp akzeptiert wird, während thenReturnein Nicht-Platzhaltertyp verwendet wird, der erfasst werden muss. Aus Mockitos OngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
quelle
Dies hilft mir teilweise auch ... aber was passiert, wenn die Liste, die Sie voraussichtlich zurückgeben, nicht leer ist?
ttati
Anstatt eine leere Liste zu haben, können Sie auch Folgendes tun: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati
32

Ich gehe davon aus, dass Sie in der Lage sein möchten, someListeinige bekannte Werte zu laden . Hier ist ein Ansatz, der Answer<T>zusammen mit einer Vorlagen-Hilfsmethode verwendet wird, um alles typsicher zu machen:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}
Mühle
quelle
17

Ich habe gestern das Gleiche getroffen. Beide Antworten von @ nondescript1 und @millhouse haben mir geholfen, eine Problemumgehung zu finden. Ich habe so ziemlich den gleichen Code wie @millhouse verwendet, außer dass ich ihn etwas allgemeiner gemacht habe, weil mein Fehler nicht durch a verursacht wurde java.util.List, sondern durch den com.google.common.base.Optional. Meine kleine Hilfsmethode erlaubt daher jeden Typ Tund nicht nur List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

Mit dieser Hilfsmethode können Sie schreiben:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

Dies kompiliert einwandfrei und macht das gleiche wie die thenReturn(...)Methode.

Weiß jemand, ob der Fehler, den der Java-Compiler ausgibt, ein Compiler-Fehler ist oder ob der Code wirklich falsch ist?

Marek Radonsky
quelle
Dies scheint unkompliziert, einfach und, soweit ich das beurteilen kann, richtig zu sein. Ich bin mir nicht sicher, warum Mockito so etwas nicht anbietet ....... es sei denn?
Vacao
14
In Java 8 kann es verkürzt werden : Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList), also keine Notwendigkeit für die Dienstprogrammmethode
fikovnik
1
@fikovnik Was für eine großartige Entdeckung "thenAnswer"!
Borjab
5

Ich verwandle den Kommentar von fikovnik hier in eine Antwort, um ihn besser sichtbar zu machen, da ich denke, dass dies die eleganteste Lösung mit Java 8+ ist.

In der Mockito-Dokumentation wird empfohlen, doReturn()(wie in der akzeptierten Antwort vorgeschlagen) nur als letzten Ausweg zu verwenden.

Um den in der Frage beschriebenen Compilerfehler zu umgehen, when()kann thenAnswer()stattdessen der empfohlene Mockito- Ansatz mit und einem Lambda (anstelle einer Hilfsmethode) verwendet werden:

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
ein anderer Knoten
quelle
Obwohl es keine Fehler bei der Kompilierung gibt, ist die zurückgegebene Liste leer, selbst wenn wir eine Liste mit Einträgen übergeben.
Venkatesh Kolla - user2742897
0

Obwohl die von Marek Radonsky vorgeschlagene Dienstprogrammmethode funktioniert, gibt es auch eine andere Option, für die nicht einmal der (meiner Meinung nach seltsam aussehende) Lambda-Ausdruck erforderlich ist, den fikovnik vorgeschlagen hat:

Wie diese Antwort auf eine ähnliche Frage zeigt, können Sie auch Folgendes verwenden:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Andreas Siegel
quelle