Mockito је фрејмворк отвореног кода за креирање мок-објеката у модуларним тестовима Java и Kotlin, који омогућава изолацију тестираног кода од спољашњих зависности. Помоћу њега програмер замењује стварна репозиторијуме, API клијенте и базе података контролисаним заглушкама са задатим понашањем. Према подацима Mockito.org, библиотека се користи у више од 60% Java пројеката који примењују модуларно тестирање.
Главно
Mockito је библиотека отвореног кода за креирање мок-објеката (заглушки) у модуларним тестовима на Java, Kotlin и другим JVM језицима. За разлику од JUnit, који је одговоран за покретање тестова, Mockito решава проблем изолације — замењује стварне зависности тестиране класе предвидљивим објектима.
Без мокова, тестирање метода који приступа бази података или спољном API-ју захтева подешавање стварног окружења — покретање базе, сервера. Mockito замењује ове зависности објектима са фиксним понашањем: метод repository.findById(1) увек враћа одређени User објекат, без приступа бази.
Архитектура Mockito се заснива на обрасцу Proxy (за интерфејсе и класе). Библиотека генерише подкласу или прокси за наведени тип и пресреће све позиве метода, враћајући подразумеване вредности или вредности задате кроз when().thenReturn().
Принцип рада Mockito се заснива на три основне операције: креирање мока, подешавање понашања (stubbing) и провера позива (verification). Свака операција користи статичке методе из класе org.mockito.Mockito — најоптерећеније класе у екосистему Java према статистици Maven Central. Сви позиви метода мока се бележе у меморији, што омогућава каснију проверу кроз verify.
Типичан тест са Mockito се састоји од три фазе: Arrange — креирање мокова и подешавање стабова кроз when().thenReturn(), Act — позив тестираног метода, Assert — провера резултата кроз assertEquals и verify(mock). Овај приступ се назива AAA (Arrange-Act-Assert).
Размотримо једноставан тест у коме Mockito замењује репозиторијум корисника. Метод when().thenReturn() подешава мок тако да позив findById враћа унапред припремљен User објекат.
// Креирамо мок репозиторијума
UserRepository mockRepo = mock(UserRepository.class);
// Подешавамо понашање: при findById(1) врати корисника
when(mockRepo.findById(1)).thenReturn(new User("Alice"));
// Проверавамо да ли је метод стварно позван
User result = mockRepo.findById(1);
assertEquals("Alice", result.getName());
verify(mockRepo).findById(1);
Mockito пружа два начина креирања мокова: статички метод mock(Class) и анотацију @Mock са иницијализацијом кроз MockitoAnnotations.openMocks(). Први начин је компактан за један-два мока, други је згодан када је зависности много — анотације смањују boilerplate код.
Метод mock() прима класу и враћа објекат-заглушку који се може подесити кроз when().thenReturn(). Сви неподешени методи враћају подразумеване вредности: 0 за бројеве, false за boolean, null за објекте.
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);
Анотација @Mock у комбинацији са @ExtendWith(MockitoExtension.class) аутоматски креира мокове за сва поља тестне класе. Екстензија MockitoExtension је одговорна за иницијализацију пре сваког теста.
@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 — процес одређивања шта метод мока треба да врати при позиву са одређеним аргументима. Основна синтакса: when(mock.method(args)).thenReturn(value). За различите сценарије, Mockito нуди неколико варијанти then-метода.
| Метод | Намена |
|---|---|
| thenReturn(value) | Увек враћа наведену вредност |
| thenThrow(exception) | Баца изузетак при позиву |
| thenAnswer(answer) | Израчунава повратну вредност динамички |
| thenCallRealMethod() | Позива стварни метод (делимични мок) |
Када повратна вредност зависи од аргумената позива, користи се thenAnswer са ламбдом. Ово је корисно за имитацију рада са стварним подацима — на пример, генерисање ID-ја на основу прослеђеног објекта.
when(repository.save(any())).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(42);
return user;
});
Verify је јединствена могућност Mockito-а коју старије библиотеке мок-објеката (EasyMock, jMock) не пружају.
Verify чини тестове поузданијим, јер проверава не само повратну вредност, већ и споредне ефекте — позиве метода који не враћају резултат (void методи). Метод verify(mock).methodName(args) проверава да ли је одређени метод мока позван са наведеним аргументима. Ово омогућава тестирање не само резултата, већ и процеса — чињенице обраћања зависности.
Подразумевано, verify проверава да је метод позван тачно једанпут. Ако је потребан други број — користе се times(n), atLeast(n), never() и други модификатори из класе Mockito.
// Провера броја позива
verify(repository, times(3)).save(any());
verify(repository, never()).delete(any());
verify(repository, atLeastOnce()).findById(1);
// Провера редоследа позива
InOrder inOrder = inOrder(repository);
inOrder.verify(repository).save(any());
inOrder.verify(repository).flush();
Када треба проверити са којим тачно објектом је метод позван, користи се ArgumentCaptor. Он хвата вредност аргумента током позива и омогућава проверу његових поља појединачно. ArgumentCaptor је посебно користан када тестирани код ствара објекат унутар себе и прослеђује га зависности — не можете овај објекат проверити другачије.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());
@Mock и @InjectMocks — две кључне анотације Mockito-а које значајно смањују boilerplate код. @Mock креира мок за поље, а @InjectMocks убризгава све мокове из тестне класе у тестирани објекат кроз конструктор, сетер или поље.
Механизам @InjectMocks покушава да убризга зависности следећим редоследом: конструктор са највећим бројем аргумената, сетер по типу, приватно поље. Ако ниједан начин не успе — објекат остаје са null зависностима и тест пада са NullPointerException.
Важно је разумети: @InjectMocks не анализира типове поља — подмеће било који мок компатибилан по типу. Ако у класи постоје два поља истог типа — Mockito може убризгати погрешан мок. У таквим случајевима препоручује се коришћење експлицитног конструктора са прослеђивањем мокова.
У Android развоју, Mockito се користи заједно са JUnit за тестирање ViewModel, Repository и UseCase. Пошто ове класе раде на JVM без Android контекста, Mockito замењује њихове зависности — Room DAO, Retrofit API, SharedPreferences — стабовима са предвидљивим понашањем.
За додавање Mockito у Android пројекат, довољно је додати зависност mockito-core или mockito-inline (ова друга подржава моковање final класа и статичких метода). Верзија 5.12.0 (2024) укључује подршку за Java 21 и побољшану интеграцију са JUnit 5.
// build.gradle.kts (модул)
dependencies {
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}
Раније је за моковање статичких метода и конструктора био потребан PowerMock — проширење које ради кроз бајткод-инструментацију. Почев од Mockito 5.x са mockito-inline, ова могућност је уграђена директно: mockStatic(ClassName.class) омогућава моковање статичких метода без додатних библиотека.
Типичан сценарио: ViewModel позива метод репозиторијума и трансформише резултат у UI стање. Mockito замењује репозиторијум, а тест проверава да ViewModel коректно обрађује успешан одговор и грешку. Код архитектуре Clean Architecture, мокови се креирају за сваки слој: DataSource, Repository и UseCase — ово омогућава тестирање сваког слоја изоловано.
Често постављана питања
Mockito — библиотека за Java и Kotlin која користи прокси и рефлексију. MockK — Kotlin-first библиотека са подршком за корутине, extension функције и final класе без додатне конфигурације.
Од Mockito 2.1, моковање final класа је подржано кроз opt-in. У верзији 5.x (mockito-inline) ово је укључено подразумевано. Довољно је додати зависност mockito-inline и користити стандардни mock() метод.
Spy — делимични мок који подразумевано позива стварне методе, али омогућава преправљање неких од њих кроз when().thenReturn(). Spy је користан за тестирање legacy кода када се не може преписати цела класа.
thenReturn увек враћа исту вредност, без обзира на аргументе. thenAnswer израчунава повратну вредност на основу позива — аргумената, самог мока, стања. За динамичке одговоре увек користите thenAnswer.
Verify проверава не само резултат, већ и процес — чињеницу обраћања зависности. Ово је критично за сервисе који морају да сачувају податке или пошаљу обавештења. Без verify, тест неће открити да метод није позвао save() или send().
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође