Mockito este un framework open-source pentru crearea obiectelor mock în testele unitare Java și Kotlin, care permite izolarea codului testat de dependențele externe. Cu ajutorul său, dezvoltatorul înlocuiește repository-urile reale, clienții API și bazele de date cu substituenți controlați cu un comportament prestabilit. Conform datelor Mockito.org, biblioteca este utilizată în peste 60% din proiectele Java care aplică testarea unitară.
Principalele
Mockito este o bibliotecă open-source pentru crearea obiectelor mock (substituenți) în testele unitare în Java, Kotlin și alte limbaje JVM. Spre deosebire de JUnit, care se ocupă de rularea testelor, Mockito rezolvă problema izolării — înlocuiește dependențele reale ale clasei testate cu obiecte previzibile.
Fără mockuri, testarea unei metode care accesează o bază de date sau un API extern necesită configurarea unui mediu real — implementarea bazei de date, pornirea unui server. Mockito înlocuiește aceste dependențe cu obiecte cu comportament fix: metoda repository.findById(1) returnează întotdeauna un obiect User specific, fără a accesa baza de date.
Arhitectura Mockito se bazează pe modelul Proxy (pentru interfețe și clase). Biblioteca generează o subclasă sau un proxy pentru tipul specificat și interceptează toate apelurile de metode, returnând valori implicite sau valori stabilite prin when().thenReturn().
Principiul de funcționare a Mockito se bazează pe trei operațiuni de bază: crearea mock-ului, configurarea comportamentului (stubbing) și verificarea apelurilor (verification). Fiecare operațiune utilizează metode statice din clasa org.mockito.Mockito — cea mai încărcată clasă din ecosistemul Java conform statisticilor Maven Central. Toate apelurile metodelor mock-ului sunt înregistrate în memorie, permițând verificarea ulterioară prin verify.
Un test tipic cu Mockito constă în trei faze: Arrange — crearea mockurilor și configurarea stuburilor prin when().thenReturn(), Act — apelarea metodei testate, Assert — verificarea rezultatului prin assertEquals și verify(mock). Această abordare se numește AAA (Arrange-Act-Assert).
Să examinăm un test simplu în care Mockito înlocuiește repository-ul de utilizatori. Metoda when().thenReturn() configurează mock-ul astfel încât apelul findById să returneze un obiect User pregătit în prealabil.
// Creăm un mock de repository
UserRepository mockRepo = mock(UserRepository.class);
// Configurăm comportamentul: la findById(1) returnează utilizatorul
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// Verificăm că metoda a fost într-adevăr apelată
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockito oferă două moduri de a crea mockuri: metoda statică mock(Class) și adnotarea @Mock cu inițializare prin MockitoAnnotations.openMocks(). Prima metodă este compactă pentru unul-două mockuri, a doua este convenabilă când dependențele sunt multe — adnotările reduc codul boilerplate.
Metoda mock() primește o clasă și returnează un obiect substituent care poate fi configurat prin when().thenReturn(). Toate metodele neconfigurate returnează valori implicite: 0 pentru numere, false pentru boolean, null pentru obiecte.
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
Adnotarea @Mock împreună cu @ExtendWith(MockitoExtension.class) creează automat mockuri pentru toate câmpurile clasei de test. Extensia MockitoExtension se ocupă de inițializare înainte de fiecare 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 — procesul de determinare a valorii pe care metoda mock-ului trebuie să o returneze la apelarea cu argumente specifice. Sintaxa de bază: when(mock.method(args)).thenReturn(value). Pentru diferite scenarii, Mockito oferă mai multe variante de metode then.
| Metodă | Destinație |
|---|---|
| thenReturn(value) | Returnează întotdeauna valoarea specificată |
| thenThrow(exception) | Aruncă o excepție la apelare |
| thenAnswer(answer) | Calculează valoarea returnată dinamic |
| thenCallRealMethod() | Apelează metoda reală (mock parțial) |
Când valoarea returnată depinde de argumentele apelului, se utilizează thenAnswer cu o lambda. Acest lucru este util pentru simularea lucrului cu date reale — de exemplu, generarea unui ID pe baza obiectului transmis.
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verify este o capacitate unică a Mockito, pe care bibliotecile mai vechi de obiecte mock (EasyMock, jMock) nu o oferă.
Verify face testele mai fiabile, deoarece verifică nu doar valoarea returnată, ci și efectele secundare — apelurile metodelor care nu returnează un rezultat (metode void). Metoda verify(mock).methodName(args) verifică dacă o anumită metodă a mock-ului a fost apelată cu argumentele specificate. Aceasta permite testarea nu doar a rezultatului, ci și a procesului — faptul accesării dependenței.
În mod implicit, verify verifică că metoda a fost apelată exact o dată. Dacă este necesar un alt număr — se utilizează times(n), atLeast(n), never() și alți modificatori din clasa Mockito.
// Verificarea numărului de apeluri
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// Verificarea ordinii apelurilor
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
Când trebuie să verificăm cu ce obiect exact a fost apelată metoda, se utilizează ArgumentCaptor. Acesta captează valoarea argumentului în timpul apelului și permite verificarea câmpurilor sale separat. ArgumentCaptor este util în special când codul testat creează un obiect în interior și îl transmite dependenței — nu puteți verifica acest obiect în alt mod.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mock și @InjectMocks — două adnotări cheie ale Mockito care reduc semnificativ codul boilerplate. @Mock creează un mock pentru un câmp, iar @InjectMocks injectează toate mockurile din clasa de test în obiectul testat prin constructor, setter sau câmp.
Mecanismul @InjectMocks încearcă să injecteze dependențele în următoarea ordine: constructorul cu cel mai mare număr de argumente, setter după tip, câmp privat. Dacă niciun mod nu funcționează — obiectul rămâne cu dependențe null, iar testul eșuează cu NullPointerException.
Este important de înțeles: @InjectMocks nu analizează tipurile câmpurilor — înlocuiește orice mock compatibil ca tip. Dacă în clasă există două câmpuri de același tip — Mockito poate injecta mock-ul greșit. În astfel de cazuri, se recomandă utilizarea unui constructor explicit cu transmiterea mockurilor.
În dezvoltarea Android, Mockito este utilizat împreună cu JUnit pentru testarea ViewModel, Repository și UseCase. Deoarece aceste clase rulează pe JVM fără context Android, Mockito înlocuiește dependențele lor — Room DAO, Retrofit API, SharedPreferences — cu stuburi cu comportament previzibil.
Pentru a adăuga Mockito într-un proiect Android, este suficient să adăugați dependența mockito-core sau mockito-inline (aceasta din urmă suportă mockarea claselor final și a metodelor statice). Versiunea 5.12.0 (2024) include suport pentru Java 21 și integrare îmbunătățită cu JUnit 5.
// build.gradle.kts (modul)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
Anterior, pentru mockarea metodelor statice și a constructorilor era necesar PowerMock — o extensie care funcționa prin instrumentarea bytecodului. Începând cu Mockito 5.x cu mockito-inline, această funcționalitate este încorporată direct: mockStatic(ClassName.class) permite mockarea metodelor statice fără biblioteci suplimentare.
Scenariu tipic: ViewModel apelează metoda repository-ului și transformă rezultatul în stare UI. Mockito înlocuiește repository-ul, iar testul verifică că ViewModel procesează corect răspunsul de succes și eroarea. În arhitectura Clean Architecture, mockurile sunt create pentru fiecare strat: DataSource, Repository și UseCase — aceasta permite testarea fiecărui strat în mod izolat.
Întrebări frecvente
Mockito este o bibliotecă pentru Java și Kotlin care utilizează proxy și reflecție. MockK este o bibliotecă Kotlin-first cu suport pentru corutine, funcții de extensie și clase final fără configurare suplimentară.
Începând cu Mockito 2.1, mockarea claselor final este suportată prin opt-in. În versiunea 5.x (mockito-inline) aceasta este activată implicit. Este suficient să adăugați dependența mockito-inline și să utilizați metoda standard mock().
Spy este un mock parțial care, în mod implicit, apelează metodele reale, dar permite suprascrierea unora dintre ele prin when().thenReturn(). Spy este util pentru testarea codului legacy când nu se poate rescrie întreaga clasă.
thenReturn returnează întotdeauna aceeași valoare, indiferent de argumente. thenAnswer calculează valoarea returnată pe baza invocării — argumentele apelului, mock-ul în sine, starea. Pentru răspunsuri dinamice, utilizați întotdeauna thenAnswer.
Verify verifică nu doar rezultatul, ci și procesul — faptul accesării dependenței. Acest lucru este critic pentru serviciile care trebuie să salveze date sau să trimită notificări. Fără verify, testul nu va detecta că metoda nu a apelat save() sau send().
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și