Mockito è un framework open source per creare oggetti mock nei test unitari Java e Kotlin, che consente di isolare il codice testato dalle dipendenze esterne. Con il suo aiuto, lo sviluppatore sostituisce repository reali, client API e database con stub controllati dal comportamento predefinito. Secondo Mockito.org, la libreria è utilizzata in oltre il 60% dei progetti Java che utilizzano test unitari.
Punti chiave
Mockito è una libreria open source per creare oggetti mock (stub) nei test unitari per Java, Kotlin e altri linguaggi JVM. A differenza di JUnit, che si occupa dell’esecuzione dei test, Mockito risolve il problema dell’isolamento — sostituisce le dipendenze reali della classe testata con oggetti prevedibili.
Senza mock, testare un metodo che accede a un database o a un’API esterna richiede la configurazione di un ambiente reale — distribuzione di un database, avvio di un server. Mockito sostituisce queste dipendenze con oggetti dal comportamento fisso: il metodo repository.findById(1) restituisce sempre un oggetto User specifico senza accedere al database.
L’architettura di Mockito si basa sul pattern Proxy (per interfacce e classi). La libreria genera una sottoclasse o un proxy per il tipo specificato e intercetta tutte le chiamate ai metodi, restituendo valori predefiniti o valori impostati tramite when().thenReturn().
Il principio di funzionamento di Mockito si basa su tre operazioni fondamentali: creazione del mock, configurazione del comportamento (stubbing) e verifica delle chiamate (verification). Ogni operazione utilizza metodi statici della classe org.mockito.Mockito — la classe più scaricata nell’ecosistema Java secondo le statistiche di Maven Central. Tutte le chiamate ai metodi del mock vengono registrate in memoria, consentendo la successiva verifica tramite verify.
Un test tipico con Mockito si compone di tre fasi: Arrange — creazione dei mock e configurazione degli stub tramite when().thenReturn(), Act — chiamata al metodo testato, Assert — verifica del risultato tramite assertEquals e verify(mock). Questo approccio è chiamato AAA (Arrange-Act-Assert).
Consideriamo un test semplice in cui Mockito sostituisce un repository di utenti. Il metodo when().thenReturn() configura il mock in modo che la chiamata findById restituisca un oggetto User preparato in precedenza.
// Creazione di un mock del repository
UserRepository mockRepo = mock(UserRepository.class);
// Configurazione del comportamento: findById(1) restituisce un utente
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// Verifica che il metodo sia stato effettivamente chiamato
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockito fornisce due modi per creare mock: il metodo statico mock(Class) e l’annotazione @Mock con inizializzazione tramite MockitoAnnotations.openMocks(). Il primo approccio è compatto per uno o due mock, il secondo è comodo quando ci sono molte dipendenze — le annotazioni riducono il codice boilerplate.
Il metodo mock() accetta una classe e restituisce un oggetto stub che può essere configurato tramite when().thenReturn(). Tutti i metodi non configurati restituiscono valori predefiniti: 0 per i numeri, false per boolean, null per gli oggetti.
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
L’annotazione @Mock combinata con @ExtendWith(MockitoExtension.class) crea automaticamente mock per tutti i campi della classe di test. MockitoExtension si occupa dell’inizializzazione prima di ogni test.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void getUserShouldReturnUserFromRepo() {
when(userRepository.findById(1)).thenReturn(new User("Alice"));
User result = userService.getUser(1);
assertEquals("Alice", result.getName());
}
}
Stubbing è il processo di definizione di ciò che un metodo del mock deve restituire quando chiamato con argomenti specifici. La sintassi di base: when(mock.method(args)).thenReturn(value). Per diversi scenari, Mockito offre varie varianti di metodi then.
| Metodo | Scopo |
|---|---|
| thenReturn(value) | Restituisce sempre il valore specificato |
| thenThrow(exception) | Lancia un’eccezione quando chiamato |
| thenAnswer(answer) | Calcola il valore restituito dinamicamente |
| thenCallRealMethod() | Chiama il metodo reale (mock parziale) |
Quando il valore restituito dipende dagli argomenti della chiamata, si usa thenAnswer con una lambda. Ciò è utile per simulare il lavoro con dati reali — ad esempio, generare un ID basato sull’oggetto ricevuto.
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verify è una funzionalità unica di Mockito che le librerie di oggetti mock più datate (EasyMock, jMock) non forniscono.
Verify rende i test più affidabili perché verifica non solo il valore restituito ma anche gli effetti collaterali — chiamate a metodi che non restituiscono risultato (metodi void). Il metodo verify(mock).methodName(args) verifica se un metodo specifico del mock è stato chiamato con gli argomenti indicati. Ciò consente di testare non solo il risultato ma anche il processo — il fatto di accedere alla dipendenza.
Per impostazione predefinita, verify controlla che il metodo sia stato chiamato esattamente una volta. Se è necessario un numero diverso, si usano times(n), atLeast(n), never() e altri modificatori della classe Mockito.
// Verifica del numero di chiamate
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// Verifica dell’ordine delle chiamate
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
Quando è necessario verificare con quale oggetto esatto è stato chiamato un metodo, si usa ArgumentCaptor. Cattura il valore dell’argomento durante la chiamata e consente di verificare i suoi campi singolarmente. ArgumentCaptor è particolarmente utile quando il codice testato crea un oggetto internamente e lo passa a una dipendenza — non è possibile verificare quell’oggetto altrimenti.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mock e @InjectMocks sono due annotazioni chiave di Mockito che riducono significativamente il codice boilerplate. @Mock crea un mock per un campo, e @InjectMocks inietta tutti i mock della classe di test nell’oggetto testato tramite costruttore, setter o campo.
Il meccanismo di @InjectMocks tenta di iniettare le dipendenze nel seguente ordine: costruttore con il maggior numero di argomenti, setter per tipo, campo privato. Se nessuno di questi metodi funziona, l’oggetto rimane con dipendenze null e il test fallirà con una NullPointerException.
È importante capire: @InjectMocks non analizza i tipi dei campi — sostituisce qualsiasi mock compatibile per tipo. Se una classe ha due campi dello stesso tipo, Mockito potrebbe iniettare il mock sbagliato. In tali casi, si consiglia di utilizzare un costruttore esplicito con parametri mock.
Nello sviluppo Android, Mockito viene utilizzato insieme a JUnit per testare ViewModel, Repository e UseCase. Poiché queste classi vengono eseguite sulla JVM senza il contesto Android, Mockito sostituisce le loro dipendenze — Room DAO, Retrofit API, SharedPreferences — con stub dal comportamento prevedibile.
Per aggiungere Mockito a un progetto Android, basta aggiungere la dipendenza mockito-core o mockito-inline (quest’ultima supporta il mocking di classi final e metodi statici). La versione 5.12.0 (2024) include il supporto per Java 21 e un’integrazione migliorata con JUnit 5.
// build.gradle.kts (module)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
In precedenza, per mockare metodi statici e costruttori era necessario PowerMock — un’estensione che funzionava tramite strumentazione del bytecode. A partire da Mockito 5.x con mockito-inline, questa funzionalità è integrata direttamente: mockStatic(ClassName.class) consente di mockare metodi statici senza librerie aggiuntive.
Uno scenario tipico: un ViewModel chiama un metodo del repository e trasforma il risultato in stato UI. Mockito sostituisce il repository e il test verifica che il ViewModel gestisca correttamente sia la risposta di successo che l’errore. Quando si utilizza la Clean Architecture, vengono creati mock per ogni livello: DataSource, Repository e UseCase — ciò consente di testare ogni livello in isolamento.
Domande frequenti
Mockito è una libreria per Java e Kotlin che utilizza proxy e riflessione. MockK è una libreria Kotlin-first con supporto per coroutine, funzioni di estensione e classi final senza configurazione aggiuntiva.
A partire da Mockito 2.1, il mocking delle classi final è supportato tramite opt-in. Nella versione 5.x (mockito-inline), questa funzionalità è attivata per impostazione predefinita. Basta aggiungere la dipendenza mockito-inline e utilizzare il metodo standard mock().
Uno Spy è un mock parziale che per impostazione predefinita chiama metodi reali ma consente di sovrascriverne alcuni tramite when().thenReturn(). Spy è utile per testare codice legacy quando non è possibile riscrivere l’intera classe.
thenReturn restituisce sempre lo stesso valore, indipendentemente dagli argomenti. thenAnswer calcola il valore restituito in base all’invocazione — argomenti della chiamata, il mock stesso e lo stato. Per risposte dinamiche, usa sempre thenAnswer.
Verify verifica non solo il risultato ma anche il processo — il fatto di accedere alla dipendenza. Ciò è fondamentale per i servizi che devono salvare dati o inviare notifiche. Senza verify, il test non rileverà che un metodo non ha chiamato save() o send().
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche