Utilizzo di Mockito per deridere classi con parametri generici


280

Esiste un metodo pulito per deridere una classe con parametri generici? Di 'che devo prendere in giro una classe Foo<T>che devo passare a un metodo che prevede a Foo<Bar>. Posso fare quanto segue abbastanza facilmente:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

Supponendo che getValue()ritorni il tipo generico T. Ma avrà gattini quando in seguito lo passerò a un metodo che mi aspetto Foo<Bar>. Il casting è l'unico mezzo per farlo?

Risposte:


280

Penso che tu debba lanciarlo, ma non dovrebbe essere così male:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
Sì, ma hai ancora un avvertimento. È possibile evitare l'avvertimento?
odwl

12
@SuppressWarnings ("deselezionato")
qualidafial

18
Penso che questo sia pienamente accettabile dal momento che stiamo parlando di un oggetto simulato in un test unitario.
Magnilex,

1
@demaniak Non funziona affatto. Gli abbinamenti di argomenti non possono essere utilizzati in quel contesto.
Krzysztof Krasoń

1
@demaniak Verrà compilato correttamente, ma durante l'esecuzione del test genererà InvalidUseOfMatchersException (che è una RuntimeException)
Superole

277

Un altro modo per aggirare questo è usare @Mockinvece l'annotazione. Non funziona in tutti i casi, ma sembra molto più sexy :)

Ecco un esempio:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

Il MockitoJUnitRunnerinizializza i campi annotati con @Mock.


3
questo è deprecato in 1.9.5. :( Mi sembra molto più pulito.
Noviziato di codice

12
@CodeNovitiate Non ho trovato annotazioni di deprecazione su MockitoJUnitRunner e Mock in 1.9.5. Quindi, cosa è deprecato? (Sì, org.mockito.MockitoAnnotations.Mock è deprecato, ma dovresti usare org.mockito.Mock invece)
neu242

12
Ben fatto, questo ha funzionato perfettamente per me. Non è solo "più sexy", evita un avvertimento senza usare SuppressWarnings. Gli avvertimenti esistono per una ragione, è meglio non avere l'abitudine di sopprimerli. Grazie!
Nicole,

4
C'è una cosa che non mi piace usare al @Mockposto di mock(): i campi sono ancora nulli durante i tempi di costruzione, quindi non posso inserire dipendenze in quel momento e non posso rendere definitivi i campi. Il primo può essere risolto con un @Beforemetodo non annotato, ovviamente.
Rüdiger Schulz,

3
Per iniziare basta chiamare MockitoAnnotations.initMocks (questo);
Borjab,

42

È sempre possibile creare un'interfaccia / classe intermedia in grado di soddisfare il tipo generico che si desidera specificare. Ad esempio, se Foo fosse un'interfaccia, è possibile creare la seguente interfaccia nella classe di test.

private interface FooBar extends Foo<Bar>
{
}

In situazioni in cui Foo è una classe non finale , puoi semplicemente estendere la classe con il seguente codice e fare la stessa cosa:

public class FooBar extends Foo<Bar>
{
}

Quindi è possibile utilizzare uno degli esempi sopra con il seguente codice:

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

4
A condizione che Foosia un'interfaccia o una classe non finale, questa sembra essere una soluzione ragionevolmente elegante. Grazie.
Tim Clemons,

Ho aggiornato la risposta per includere esempi anche per le classi non finali. Idealmente dovresti codificare su un'interfaccia, ma non sarà sempre così. Buona pesca!
Dsingleton,

16

Creare un metodo di utilità di test . Utile specialmente se ne hai bisogno per più di una volta.

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

Potresti estendere la tua risposta per creare un metodo di utilità generale passando nella classe che vuoi deridere.
William Dutton,

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }non ha nemmeno bisogno di una sola soppressione :) Ma fai attenzione, Integer num = genericMock(Number.class)compila, ma lancia ClassCastException. Questo è utile solo per il G<P> mock = mock(G.class)caso più comune .
TWiStErRob,

6

Concordo sul fatto che non si dovrebbero sopprimere gli avvisi in classi o metodi in quanto si potrebbero trascurare altri avvisi soppressi accidentalmente. Ma IMHO è assolutamente ragionevole sopprimere un avviso che riguarda solo una singola riga di codice.

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

Ecco un caso interessante: il metodo riceve una raccolta generica e restituisce una raccolta generica dello stesso tipo di base. Per esempio:

Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);

Questo metodo può essere deriso con la combinazione di Mockito anyCollectionOf matcher e la risposta.

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
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.