Mockito: metodi di stubbing che restituiscono il tipo con caratteri jolly rilegati


135

Considera questo codice:

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
    }
}

Il compilatore si lamenta della linea per cui sta tentando di bloccare il comportamento dummyMethod(). Qualche suggerimento su come procedere con i metodi di stub che restituiscono un tipo con caratteri jolly limitati?


Puoi aggiornare lo snippet di codice per mostrare i tipi generici?
Mulino

1
Fatto. Ho dovuto rimuovere i tag pre e code, stavano rimuovendo <? estende Numero> dalla dichiarazione del tipo.
Shikhar Mishra,

Risposte:


190

A tale scopo puoi anche utilizzare il metodo sicuro non di tipo doReturn ,

@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());
}

come discusso sul gruppo google di Mockito.

Mentre questo è più semplice di thenAnswer, nota ancora che non è sicuro. Se sei preoccupato per la sicurezza dei tipi, la risposta di Millhouse è corretta.

dettagli aggiuntivi

Per essere chiari, ecco l'errore del compilatore osservato,

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>)

Credo che il compilatore abbia assegnato il primo tipo di carattere jolly durante la whenchiamata e quindi non posso confermare che il secondo tipo di carattere jolly nella thenReturnchiamata sia lo stesso.

Sembra che thenAnswernon si verifichi questo problema perché accetta un tipo di carattere jolly mentre accetta un tipo thenReturnnon jolly, che deve essere acquisito. Da Mockito's IngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);

anche questo mi aiuta in parte ... ma cosa succede se l'elenco che si prevede di restituire non è vuoto?
ttati,

invece di avere una lista vuota puoi anche fare: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati,

32

Suppongo che tu voglia essere in grado di caricare someListalcuni valori noti; ecco un approccio che utilizza Answer<T>insieme a un metodo di supporto basato su modelli per mantenere tutto al sicuro:

@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;
}

17

Ho colpito la stessa cosa ieri. Entrambe le risposte di @ nondescript1 e @millhouse mi hanno aiutato a trovare una soluzione alternativa. Ho praticamente usato lo stesso codice di @millhouse, tranne per il fatto che l'ho reso leggermente più generico, perché il mio errore non è stato causato da un java.util.List, ma il com.google.common.base.Optional. Il mio piccolo metodo di supporto quindi consente qualsiasi tipo Te non solo 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;
}

Con questo metodo di supporto puoi scrivere:

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

Questo si compila bene e fa la stessa cosa del thenReturn(...)metodo.

Qualcuno sa se l'errore emesso dal compilatore Java è un bug del compilatore o se il codice è davvero errato?


Questo sembra chiaro, semplice e, per quanto posso dire, corretto. Non sono sicuro del perché Mockito non fornisca qualcosa di simile a questo ....... a meno che non lo faccia?
vacao,

14
In Java 8 può essere abbreviato Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)
:,

1
@fikovnik Che grande scoperta "thenAnswer"!
Borjab,

5

Sto trasformando il commento di fikovnik in una risposta qui per dargli maggiore visibilità poiché penso che sia la soluzione più elegante usando Java 8+.

La documentazione di Mockito raccomanda di usare doReturn()(come suggerito nella risposta accettata) solo come ultima risorsa.

Invece, per aggirare l'errore del compilatore descritto nella domanda, è when()possibile utilizzare l'approccio Mockito consigliato con thenAnswer()e un lambda (anziché un metodo helper):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)

sebbene non fornisca errori di compilazione, l'elenco restituito è vuoto anche quando stiamo passando un elenco con voci.
Venkatesh Kolla - user2742897

0

Sebbene il metodo di utilità proposto da Marek Radonsky funzioni, esiste anche un'altra opzione che non richiede nemmeno l'espressione lambda (IMHO dall'aspetto strano) suggerita da fikovnik:

Come mostra questa risposta a una domanda simile, puoi anche usare quanto segue:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.