Mockito (Android)

Mockito è un framework open source per creare Mock nel test di unità automatizzato in ambiente Java. Quest’ultimo è il framework consigliato su developer.android.com e per il quale Google mette a disposizione del codice di esempio. Può essere utilizzato insieme a JUnit4 che è spesso già presente nell’ IDE Android Studio.

Logo di Mockito

Configurazione con Android Studio

Per utilizzare Mockito in Android Studio ci basta dichiarare la dipendenza di “mockito-core” con Gradle. Bisogna aggiungere al file build.gradle la seguente linea di codice:

Notiamo che verrà utilizzata la versione 2.4.0, che non è la più recente ma ci permette di utilizzare Mockito insieme a PowerMock, un altro framework che verrà analizzato in seguito.

Utilizzo di Mockito insieme a JUnit4

Quando istanziamo la classe di test bisogna aggiungere una annotazione ”@RunWith(MockitoJUnitRunner.class)” all’inizio della classe di test. Questa annotazione valida il corretto uso del framework e semplifica l’inizializzazione degli oggetti mock.
Per creare un oggetto Mock al fine di risolvere una certa dipendenza si usa “@Mock”.

Un’altra potente annotazione è “@Spy” che permette di creare quello che potremmo definire un mock parziale, poiché se non viene creato un fake per un metodo allora verrà chiamato il metodo reale. Si suppone che si lasci utilizzare il metodo reale, quando ci fidiamo del suo comportamento o quando è un codice di libreria affidabile. Se il fake per la classe spiata viene realizzato allora verrà richiamato quest’ultimo e non il reale.

Mockito method

Le API di Mockito sono innumerevoli, in questa sotto sezione vengono descritte quelle usate nei test svolti sull’App Android realizzata dall’autore di questo post. Notiamo che queste API potranno essere usate anche con PowerMock.
Iniziamo con when e thenReturn, che possono essere utilizzati per creare velocemente un fake di un metodo.

Analizziamo le parole chiave:

  • when: vuole come parametro il metodo dell’oggetto mock per il quale vogliamo creare il fake, richiamato con la “dot notation” come sopra;
  • thenReturn: ha come parametro valoreDiRitorno, l’oggetto o il valore della variabile di ritorno (e.g. valoreDiRitorno può essere un intero, oppure l’oggetto di una classe).

Analizziamo ora doNothing che viene utilizzato per i metodi void per fare in modo che l’invocazione di quel metodo non abbia nessun effetto:

Con questo codice quando oggettoMock.metodoOggetto(param) verrà richiamato, non verrà fatta nessuna operazione. Notiamo che abbiamo specificato un parametro preciso e quindi quella API funzionerà solo quando il metodo avrà in ingresso quel parametro.

In questo caso e nel precedente al posto di un parametro specifico possiamo usare anyInt(), anyString(). Ad esempio con anyInt() per qualsiasi intero passato per parametro fai una certa cosa. Nel caso di classi custom si può usare any(ClasseCustom.class), quindi per qualsiasi oggetto della classe ClasseCustom.

In alcune situazioni, durante il test di unità, potremmo essere interessati a conosce il valore dei parametri con cui è stato chiamato un metodo, sia esso con valore di ritorno oppure void. Per catturare questi valori si usa il seguente codice:

Analizziamo il codice:

  • ArgumentCaptor: è una classe di Mockito che permette di catturare i valori passati per parametro ad un metodo. Questo avviene utilizzando argumentCaptor.capture() al posto dei parametri della firma del metodo (vedi riga 3);
  • Con assertEquals verifichiamo se il parametro inserito durante l’esecuzione corrisponde a quello desiderato, per farci ritornare il valore catturato si utilizza argumentCaptor.getValue().

Con verify possiamo conteggiare il numero di volte che un metodo viene richiamato all’interno del nostro test di unità. Nei commenti viene riportato cosa fa ogni metodo.

Con il codice della riga 6 possiamo verificare se getSomeThing(…) viene chiamato n volte. Nel caso in cui questo non avviene Mockito lancia delle eccezioni autoesplicative che potremmo catturare con un try catch e poi asserire un fallimento con JUnit, come nel seguente codice. Tutte queste eccezioni sono di tipo MockitoAssertionError.

Il framework Mockito non si limita a creare mock objects e fake dei metodi di una classe, ma può anche “lanciare” un’eccezione quando viene eseguita una certa istruzione, e.g. viene invocato un metodo. La sintassi è la seguente:

Questo codice impone che quando verrà invocato il metodo child() dell’oggetto mDatabaseReference, qualsiasi sia la stringa passata per parametro “anyString()” verrà lanciata l’eccezione NullPointerException(); In pratica riusciamo ad iniettare un’eccezione durante l’esecuzione del nostro codice. Questo può essere utile per verificare se il nostro codice gestisce le eccezioni correttamente.

Vantaggi e svantaggi

Riepilogando, i vantaggi di Mockito sono:

  • fornisce un enorme quantitativo di API per testare classi e metodi pubblic;
  • la documentazione java è ben fatta ed è esaustiva, inoltre è possibile trovare molti riferimenti in letteratura;
  • è un framework molto utilizzato in Android, infatti il suo uso è consigliato su developer.android.com.

Svantaggi:

  • Il vero limite di questo framework è il non poter accedere a metodi privati o attributi privati di una classe;
  • Non è possibile creare un fake di un metodo static;
  • Può portare alla modifica del codice per scopi di testing, e.g. costringe a creare getter e setter per accedere ad attributi privati della classe da testare (come nei casi di test descritti).

Per risolvere questi problemi è possibile utilizzare un altro framework che si chiama PowerMock, che si basa anche su Mockito, ma ne estende le funzionalità.