Mockito — це фреймворк з відкритим кодом для створення mock-об'єктів у модульних тестах Java та Kotlin, який дозволяє ізолювати код, що тестується, від зовнішніх залежностей. За його допомогою розробник підміняє реальні репозиторії, API-клієнти та бази даних контрольованими заглушками із заданою поведінкою. За даними Mockito.org, бібліотека використовується більш ніж у 60% Java-проектів, які застосовують модульне тестування.
Головне
Mockito — це бібліотека з відкритим кодом для створення mock-об'єктів (заглушок) у модульних тестах на 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(мок). Такий підхід називається 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, яку не надають старіші бібліотеки mock-об'єктів (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 (module)
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 обчислює значення, що повертається, на основі invocation — аргументів виклику, самого мока, стану. Для динамічних відповідей завжди використовуйте thenAnswer.
Verify перевіряє не тільки результат, але й процес — факт звернення до залежності. Це критично для сервісів, які повинні зберігати дані або надсилати сповіщення. Без verify тест не виявить, що метод не викликав save() або send().
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також