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.