it-swarm.it

Mockito: metodi di stub che restituiscono il tipo con i jolly associati

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 che sta tentando di bloccare il comportamento di dummyMethod(). Qualche suggerimento su come procedere con i metodi di stub che restituiscono un tipo con caratteri jolly limitati?

108
mrmsra

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, tieni di nuovo presente che non è sicuro. Se sei preoccupato per la sicurezza dei tipi, la funzione di fresatura risposta è 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 chiamata when e quindi non posso confermare che il secondo tipo di carattere jolly nella chiamata thenReturn sia lo stesso.

Sembra che thenAnswer non riscontri questo problema perché accetta un tipo di carattere jolly mentre thenReturn accetta un tipo non jolly, che deve essere acquisito. Da Mockito's OngoingStubbing ,

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

Suppongo che tu voglia essere in grado di caricare someList con alcuni 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;
}
29
millhouse

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 da com.google.common.base.Optional. Il mio piccolo metodo di supporto consente quindi qualsiasi tipo T e 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 metodo thenReturn(...).

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

14
Marek Radonsky

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

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, l'approccio Mockito when() raccomandato può essere usato con thenAnswer() e un lambda (invece di un metodo helper):

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

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 questa risposta a una domanda simile mostra, puoi anche usare quanto segue:

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