Mockito — какво е, ключови понятия и принцип на работа

Автор: IT Sectr Публикувано: 2026-04-08 Време за четене: 8 мин

Mockito е рамка с отворен код за създаване на mock-обекти в модулни тестове на Java и Kotlin, която позволява изолиране на тествания код от външни зависимости. С негова помощ разработчикът замества реални хранилища, API клиенти и бази данни с контролирани заглушки със зададено поведение. Според данни на Mockito.org, библиотеката се използва в повече от 60% от Java проектите, прилагащи модулно тестване.

Основни точки

  • Mockito — библиотека за създаване на mock-обекти, които заместват реални зависимости в тестовете.
  • Mock — обект-заглушка, който имитира поведението на реален компонент.
  • Stubbing — настройка на връщаната стойност при извикване на метод на мок.
  • Verify — проверка дали методът е бил извикан с определени аргументи.
  • @InjectMocks — автоматично инжектиране на mock-зависимости в тествания обект.

Какво е Mockito?

Mockito е библиотека с отворен код за създаване на mock-обекти (заглушки) в модулни тестове на Java, Kotlin и други JVM езици. За разлика от JUnit, който отговаря за стартирането на тестове, Mockito решава проблема с изолирането — замества реалните зависимости на тествания клас с предвидими обекти.

Без мокове, тестването на метод, който има достъп до база данни или външно API, изисква настройка на реална среда — разгръщане на база данни, стартиране на сървър. Mockito замества тези зависимости с обекти с фиксирано поведение: методът repository.findById(1) винаги връща конкретен User обект, без да осъществява достъп до базата данни.

Архитектурата на Mockito се основава на модела Proxy (за интерфейси и класове). Библиотеката генерира подклас или прокси за указания тип и прихваща всички извиквания на методи, връщайки стойности по подразбиране или стойности, зададени чрез when().thenReturn().

Как работи Mockito

Принципът на работа на Mockito се основава на три основни операции: създаване на мок, настройка на поведение (stubbing) и проверка на извиквания (verification). Всяка операция използва статични методи от класа org.mockito.Mockito — най-натоварения клас в екосистемата на Java според статистиката на Maven Central. Всички извиквания на методи на мок се записват в паметта, което позволява по-късна проверка чрез verify.

Три стъпки на тест с Mockito

Типичният тест с Mockito се състои от три фази: Arrange — създаване на мокове и настройка на стабове чрез when().thenReturn(), Act — извикване на тествания метод, Assert — проверка на резултата чрез assertEquals и verify(mock). Този подход се нарича AAA (Arrange-Act-Assert).

Основен пример с мок на хранилище

Нека разгледаме прост тест, в който Mockito замества хранилището за потребители. Методът when().thenReturn() настройва мока така, че извикването findById да върне предварително подготвен User обект.

java
// Създаваме мок на хранилището
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);

Създаване на mock-обекти

Mockito предоставя два начина за създаване на мокове: статичния метод mock(Class) и анотацията @Mock с инициализация чрез MockitoAnnotations.openMocks(). Първият начин е компактен за един-два мока, вторият е удобен, когато има много зависимости — анотациите намаляват boilerplate кода.

Чрез статичния метод mock()

Методът mock() приема клас и връща обект-заглушка, който може да бъде настроен чрез when().thenReturn(). Всички ненастроени методи връщат стойности по подразбиране: 0 за числа, false за boolean, null за обекти.

java
ApiClient apiClient = mock(ApiClient.class);
Database database = mock(Database.class);

Чрез анотацията @Mock с JUnit 5

Анотацията @Mock в комбинация с @ExtendWith(MockitoExtension.class) автоматично създава мокове за всички полета на тестовия клас. Разширението MockitoExtension отговаря за инициализацията преди всеки тест.

java
@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: настройка на поведението на мок

Stubbing — процесът на определяне какво трябва да върне методът на мок при извикване с определени аргументи. Основен синтаксис: when(mock.method(args)).thenReturn(value). За различни сценарии Mockito предлага няколко варианта на then-методи.

МетодПредназначение
thenReturn(value)Винаги връща указаната стойност
thenThrow(exception)Хвърля изключение при извикване
thenAnswer(answer)Изчислява връщаната стойност динамично
thenCallRealMethod()Извиква реалния метод (частичен мок)

Динамичен отговор чрез thenAnswer

Когато връщаната стойност зависи от аргументите на извикването, се използва thenAnswer с ламбда. Това е полезно за симулиране на работа с реални данни — например генериране на ID въз основа на предадения обект.

java
when(repository.save(any())).thenAnswer(invocation -> {
    User user = invocation.getArgument(0);
    user.setId(42);
    return user;
});

Verify: проверка на взаимодействията с мок

Verify е уникална възможност на Mockito, която по-старите библиотеки за mock-обекти (EasyMock, jMock) не предоставят.

Verify прави тестовете по-надеждни, тъй като проверява не само връщаната стойност, но и страничните ефекти — извиквания на методи, които не връщат резултат (void методи). Методът verify(mock).methodName(args) проверява дали определен метод на мок е бил извикан с посочените аргументи. Това позволява тестване не само на резултата, но и на процеса — факта на обръщение към зависимостта.

Проверка на броя извиквания

По подразбиране verify проверява, че методът е бил извикан точно веднъж. Ако е необходим друг брой — се използват times(n), atLeast(n), never() и други модификатори от класа Mockito.

java
// Проверка на броя извиквания
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 е особено полезен, когато тестваният код създава обект вътрешно и го предава на зависимостта — не можете да проверите този обект по друг начин.

java
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertEquals("Alice", captor.getValue().getName());

Анотации @Mock и @InjectMocks

@Mock и @InjectMocks — две ключови анотации на Mockito, които значително намаляват boilerplate кода. @Mock създава мок за поле, а @InjectMocks инжектира всички мокове от тестовия клас в тествания обект чрез конструктор, сетер или поле.

Механизмът @InjectMocks се опитва да инжектира зависимости в следния ред: конструктор с най-голям брой аргументи, сетер по тип, частно поле. Ако никой начин не работи — обектът остава с null зависимости и тестът се проваля с NullPointerException.

Правила за използване на @InjectMocks

Важно е да се разбере: @InjectMocks не анализира типовете на полетата — поставя всеки мок, съвместим по тип. Ако в класа има две полета от един и същи тип — Mockito може да инжектира грешен мок. В такива случаи се препоръчва използването на изричен конструктор с предаване на мокове.

Mockito в Android проекти

В Android разработката Mockito се използва заедно с JUnit за тестване на ViewModel, Repository и UseCase. Тъй като тези класове работят на JVM без Android контекст, Mockito замества техните зависимости — Room DAO, Retrofit API, SharedPreferences — със стабове с предвидимо поведение.

Настройка на Gradle за Mockito

За добавяне на Mockito в Android проект е достатъчно да добавите зависимостта mockito-core или mockito-inline (последната поддържа мокване на final класове и статични методи). Версия 5.12.0 (2024) включва поддръжка за Java 21 и подобрена интеграция с JUnit 5.

kotlin
// build.gradle.kts (модул)
dependencies {
    testImplementation("org.mockito:mockito-core:5.12.0")
    testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}

Mockito и PowerMock: остаряла практика

Преди за мокване на статични методи и конструктори се изискваше PowerMock — разширение, работещо чрез инструментиране на байткод. От Mockito 5.x с mockito-inline тази функционалност е вградена директно: mockStatic(ClassName.class) позволява мокване на статични методи без допълнителни библиотеки.

Тестване на ViewModel с Mockito

Типичен сценарий: ViewModel извиква метод на хранилището и трансформира резултата в UI състояние. Mockito замества хранилището, а тестът проверява дали ViewModel правилно обработва успешния отговор и грешката. При използване на Clean Architecture се създават мокове за всеки слой: DataSource, Repository и UseCase — това позволява тестване на всеки слой изолирано.

  • success case — when(repo.getData()).thenReturn(Result.success(data)) → проверяваме state = Success(data).
  • error case — when(repo.getData()).thenReturn(Result.error(exception)) → проверяваме state = Error(message).
  • loading state — verify, че ViewModel е задал isLoading = true преди извикването на хранилището.

Често задавани въпроси

С какво Mockito се различава от MockK?

Mockito е библиотека за Java и Kotlin, използваща прокси и рефлексия. MockK е Kotlin-first библиотека с поддръжка на корутини, extension функции и final класове без допълнителна конфигурация.

Как да създадем мок за final клас в Mockito?

От Mockito 2.1 мокването на final класове се поддържа чрез opt-in. Във версия 5.x (mockito-inline) това е включено по подразбиране. Достатъчно е да добавите зависимостта mockito-inline и да използвате стандартния mock() метод.

Какво е Spy в Mockito?

Spy е частичен мок, който по подразбиране извиква реални методи, но позволява предефиниране на някои от тях чрез when().thenReturn(). Spy е полезен за тестване на legacy код, когато не можете да пренапишете целия клас.

Каква е разликата между thenReturn и thenAnswer?

thenReturn винаги връща същата стойност, независимо от аргументите. thenAnswer изчислява връщаната стойност въз основа на извикването — аргументи, самия мок, състояние. За динамични отговори винаги използвайте thenAnswer.

Защо verify е важен за тестовете с мокове?

Verify проверява не само резултата, но и процеса — факта на обръщение към зависимостта. Това е критично за услуги, които трябва да запазват данни или да изпращат уведомления. Без verify тестът няма да открие, че методът не е извикал save() или send().

Обобщение

  • Mockito — библиотека за създаване на mock-обекти, де факто стандарт за мокване в Java и Kotlin.
  • Моковете се създават чрез mock(Class) или анотация @Mock с MockitoExtension.
  • Stubbing чрез when().thenReturn() задава поведението на методите на мок.
  • Verify проверява факта и броя на извикванията на методите на мок с посочени аргументи.
  • @InjectMocks автоматично инжектира моковете в тествания обект.
  • Android интеграция — Mockito се използва за тестване на ViewModel, Repository и UseCase.
  • ArgumentCaptor улавя аргументите на извикване за детайлна проверка на полетата на обекта.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също