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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также