Как работает Mockito для тестирования в Java?

Ответ

Mockito — это библиотека для модульного тестирования в Java, которая создает mock-объекты (заглушки) для замены реальных зависимостей. Это позволяет изолировать тестируемый код и проверять его взаимодействие с другими объектами.

Основные концепции и API:

  1. Создание мока:

    List<String> mockedList = Mockito.mock(List.class);

    Или с использованием аннотаций (требует MockitoAnnotations.openMocks(this) или @ExtendWith(MockitoExtension.class) в JUnit 5):

    @Mock
    List<String> mockedList;
  2. Задание поведения (stubbing):

    when(mockedList.get(0)).thenReturn("first");
    when(mockedList.get(anyInt())).thenThrow(new RuntimeException());
  3. Проверка вызовов (verification):

    verify(mockedList).get(0); // Проверяет, что метод был вызван 1 раз
    verify(mockedList, times(2)).add(anyString()); // Проверяет точное количество вызовов
    verify(mockedList, never()).clear(); // Проверяет, что метод не вызывался
  4. Аргументные матчеры: any(), eq(), contains() и др. для гибкой проверки аргументов.

Пример полного теста:

@ExtendWith(MockitoExtension.class)
class ServiceTest {
    @Mock
    private DependencyRepository repository;
    @InjectMocks
    private ServiceUnderTest service;

    @Test
    void testServiceCall() {
        // Задаем поведение мока
        when(repository.findById(1L)).thenReturn(new Entity("test"));

        // Вызываем тестируемый метод
        String result = service.process(1L);

        // Проверяем результат и взаимодействие
        assertEquals("processed: test", result);
        verify(repository).findById(1L);
    }
}

Spy vs Mock: @Spy создает частичный мок на реальном объекте, позволяя вызывать реальные методы, если их поведение не переопределено.

Ответ 18+ 🔞

Ну, смотри, есть такая штука — Mockito. Это, блядь, библиотека для тестирования в Java, которая умеет создавать эти ваши mock-объекты, то есть, по сути, заглушки. Зачем? А чтобы, сука, отвязаться от реальных зависимостях, которые могут быть медленными, глючными или просто не нужны в конкретном тесте. Изолировал код, который проверяешь, и смотришь, как он общается с другими — вот и вся магия, ёпта.

Основные штуки, которые надо знать:

  1. Создание мока: Ну, элементарно, Ватсон. Либо в лоб:

    List<String> mockedList = Mockito.mock(List.class);

    Либо через аннотации, как цивилизованные люди. Только не забудь потом MockitoAnnotations.openMocks(this) запустить, а в JUnit 5 можно @ExtendWith(MockitoExtension.class) на класс повесить.

    @Mock
    List<String> mockedList;
  2. Задаём поведение (stubbing): Тут всё просто: говоришь моку — «когда с тобой вот это сделают, верни вот это». Или, наоборот, «взбрыкни исключением».

    when(mockedList.get(0)).thenReturn("first");
    when(mockedList.get(anyInt())).thenThrow(new RuntimeException());

    Главное — не перепутай порядок, а то будешь потом, как Герасим, метаться и думать: «Что же я, мудак, сделал?».

  3. Проверка вызовов (verification): А вот это, блядь, самое важное. Написал код, он что-то там вызвал, а ты должен убедиться, что вызов был, и именно такой, как надо.

    verify(mockedList).get(0); // Проверяет, что метод вызвали ровно один раз
    verify(mockedList, times(2)).add(anyString()); // А тут — что два раза, ни больше, ни меньше
    verify(mockedList, never()).clear(); // А этот, сука, вообще не должен был трогаться!
  4. Аргументные матчеры: Это такие хитрожопые штуки вроде any(), eq(), contains(). Позволяют не забивать голову точными значениями, а проверять по шаблону. Удобно, ебать.

Вот тебе полный пример, чтоб было понятно, как это в кучу собирается:

@ExtendWith(MockitoExtension.class)
class ServiceTest {
    @Mock
    private DependencyRepository repository; // Заглушка для репозитория
    @InjectMocks
    private ServiceUnderTest service; // Сюда моки автоматом воткнутся

    @Test
    void testServiceCall() {
        // Говорим заглушке: «Чувак, когда вызовут findById с единичкой — верни вот этот объект»
        when(repository.findById(1L)).thenReturn(new Entity("test"));

        // А теперь дергаем наш сервис, который внутри должен использовать репозиторий
        String result = service.process(1L);

        // Сперва проверяем, что результат правильный
        assertEquals("processed: test", result);
        // А потом — что репозиторий таки дернули ровно один раз с нужным аргументом
        verify(repository).findById(1L);
    }
}

И ещё про Spy vs Mock: Есть такая аннотация @Spy. Это, блядь, не полная заглушка, а такой полупидор. Создаёшь её на реальном объекте, и он по умолчанию ведёт себя как настоящий. Но если ты ему явно не пропишешь, что делать в конкретном случае — он вызовет реальный метод. Полезно, когда надо замокать только один метод в классе, а остальные оставить живыми. Но осторожно, а то ненароком реальную базу данных дернешь в тесте — будет тебе волнение ебать.