Come usare Mockito quando non possiamo passare un object fittizio a un’istanza di una class

Supponiamo che io abbia una class del genere:

public class MyClass { Dao dao; public String myMethod(Dao d) { dao = d; String result = dao.query(); return result; } } 

Voglio testarlo con il mockito. Così creo un object fittizio e chiamo il metodo per testarlo in questo modo:

 Dao mock = Mockito.mock(Dao.class); Mockito.when(mock.myMethod()).thenReturn("ok"); new MyClass().myMethod(mock); 

Ma, supponiamo che io abbia una class del genere:

 public class MyClass { Dao dao = new Dao(); public String myMethod() { String result = dao.query(); return result; } } 

Ora non posso passare il mio scherzo come argomento, quindi come posso testare il mio metodo? Qualcuno può mostrare un esempio?

Fondamentalmente, stai cercando di sostituire un campo privato con un’implementazione alternativa, il che significa che violerai l’incapsulamento. L’unica altra opzione è quella di ristrutturare la class o il metodo, per renderlo meglio progettato per il test.

Ci sono molte risposte brevi nei commenti, quindi li sto aggregando qui (e aggiungendo un paio di miei) come Wiki della comunità. Se hai qualche alternativa, sentiti libero di aggiungerli qui.

Ristruttura la class

  • Crea un setter per il campo in questione o rilassa la visibilità del campo.
  • Creare un override per l’iniezione delle dipendenze o un metodo statico che accetta un DAO e rendere delegato il metodo di istanza pubblico. Prova invece il metodo più flessibile.

     public String myMethod() { return myMethod(dao); } String myMethod(Dao dao) { /* real implementation here */ } 
  • Aggiungi un overload di costruttore o un metodo factory statico che sostituisce i campi privati ​​per motivi di testing.
  • Completamente strutturare la class per l’ iniezione di dipendenza . ( Sotirios Delimanolis , EJK )

Si noti che alcuni di questi possono essere privati ​​dei pacchetti per il test, se si mettono i test nello stesso pacchetto Java (possibilmente in un albero dei sorgenti separato). In tutti i casi, i buoni nomi e la documentazione sono utili per chiarire le vostre intenzioni.

Incapsulamento violaceo

  • Usa la riflessione per impostare i campi privati ​​nella class. ( kinbiko – vedi risposta )
  • Usa PowerMockito per sostituire il costruttore Dao con una simulazione di tua scelta. ( Dave Newton )

cosa ne pensi di questo?

 public class MyClassTest { MyClass myClass = new MyClass(); Dao dao = Mockito.mock(Dao.class); public void testMyMethod() { Field field = myClass.getClass().getDeclaredField("dao"); field.setAccessible(true); field.set(myClass, dao); //Do the test... } } 

EDIT: Come menzionato nei commenti, si presume che non si cambi il nome del campo dao . Potrebbe essere una buona idea quindi ottenere tutti i campi Field[] fields = myClass.getClass().getDeclaredFields(); e scorrere su di loro, ottenendo il campo (s) di tipo Dao. Quindi procedere come sopra. In questo modo il tuo test non dipende più dal nome del tuo campo.