2015-04-29 25 views
14

sono rimasto molto sorpreso di scoprire che seguente semplice esempio di codice non funziona per tutte le versioni Mockito> 1.8.5Mockito @InjectMocks non funziona per i campi con lo stesso tipo

@RunWith(MockitoJUnitRunner.class) 
public class MockitoTest { 

    @Mock(name = "b2") 
    private B b2; 

    @InjectMocks 
    private A a; 

    @Test 
    public void testInjection() throws Exception { 
     assertNotNull(a.b2); //fails 
     assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here 
    } 

    static class A{ 
     private B b1; 
     private B b2; 
    } 

    interface B{} 
} 

In javadocs (http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) c'è una citazione:

Nota 1: Se si dispone di campi con lo stesso tipo (o lo stesso cancellazione), è meglio nominare tutti @Mock campi annotati con i campi corrispondenti, altrimenti Mockito potrebbe far confusione e l'iniezione non accadrà.

Vuol dire che se ho diversi campi con stesso tipo non posso prendere in giro solo uno di loro, ma piuttosto dovrebbe definire @Mock per TUTTI campi con lo stesso tipo? E 'noto il limite e c'è qualche ragione per cui non è stato ancora risolto? Dovrebbe essere semplice corrispondere ai nomi dei campi @Mock, non è vero?

risposta

17

Risulta Mockito utilizza un algoritmo descritto in their JavaDoc

Se ho capito bene, lo farà Ordina al tipo (in questo caso solo 1 B) e quindi ordinare il nome (nessun cambiamento qui). Alla fine si inietterà usando the OngoingInjector interface implementation, che sembra cercare il primo campo e iniettarlo.

Poiché hai solo 1 B definito e ci sono 2 campi di B in Mock, vedrà la corrispondenza della prima istanza al campo e si fermerà. Questo perché mocks.size() == 1 in NameBasedCandidateFilter . Pertanto smetterà di filtrare e iniettarlo direttamente. Se crei più mock dello stesso tipo, verranno ordinati in Nome e iniettati di conseguenza.

Sono riuscito a farlo funzionare quando ho creato più mock (ma meno del numero di campi) di un tipo specifico.

@RunWith(MockitoJUnitRunner.class) 
public class MockitoTest { 

    @Mock(name = "b2") 
    private B b2; 

    @Mock(name = "b3") 
    private B b3; 

    @InjectMocks 
    private A a; 

    @Test 
    public void testInjection() { 
     System.out.println(this.a); 
    } 

    static class A { 

     private B b1; 

     private B b2; 

     private B b3; 
    } 

    interface B { 
    } 
} 

Ciò correttamente iniettare b2 in a.b2 e b3 in a.b3 anziché a.b1 e a.b2 (i primi 2 campi definiti in A).

È sempre possibile lasciare un problema GitHub nel proprio repository con un miglioramento o una modifica dell'algoritmo di filtraggio dell'iniezione per poter essere esaminato.

+1

In realtà, l'iniezione potrebbe essere resa un po 'più intelligente per questi casi legittimi, ma la sicurezza è obbligatoria. Le iniezioni avvengono automaticamente, quindi Mockito dovrebbe comportarsi in modo piuttosto riluttante per evitare di fare cose cattive.Inoltre, se l'iniezione è complicata, il tuo oggetto è troppo complicato o deve seguire un altro modello di creazione (un costruttore di Joshua Bloch per esempio) – Brice

+0

Questo è un problema noto e il tuo codice sopra è un aggiramento quando esistono interfacce di derisione dello stesso tipo nel sistema sotto test. – alltej

0

Questo è documentato in mockito come soluzione, se esistono più mock dello stesso tipo. Non risolve l'implementazione in base al nome fornito (ad esempio @Mock(name = "b2")). L'algoritmo utilizzato per risolvere l'implementazione è il nome del campo della dipendenza iniettata. Quindi il tuo codice sopra verrà risolto correttamente (b2 => e b3 =>@Mock private B b3).

L'altra soluzione alternativa consiste nell'utilizzare l'iniezione del costruttore che è il metodo consigliato per l'iniezione delle dipendenze.

Problemi correlati