Как мокать void методы в Mockito: гайд для новичков с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему void методы — это головная боль для новичков?

Вы когда-нибудь писали юнит-тест на Java с Mockito и вдруг натыкались на стену? Вроде всё просто: мокаешь объект, настраиваешь поведение для методов, которые возвращают что-то (строку, число, список). Но как только доходит до void методов — тех, которые ничего не возвращают, а только делают что-то внутри (например, сохраняют данные, отправляют письмо, логируют действие) — начинается ступор.

Почему? Потому что привычный when(mock.someMethod()).thenReturn(something) здесь не работает. Компилятор просто не даст вам написать when(mock.voidMethod()).thenReturn(...) — void метод ничего не возвращает, и thenReturn тут бессмыслен.

Именно этот вопрос — "How to mock void methods with Mockito" — набрал тысячи просмотров на Stack Overflow. И сегодня мы разберём его раз и навсегда. Вы научитесь не просто мокать void методы, но и делать это правильно, красиво и без боли.

Поехали! 🚀

Что такое void метод с точки зрения тестирования?

Void метод — это метод, который выполняет действие, но не возвращает результат. Например:

public class EmailService {
    public void sendWelcomeEmail(String email) {
        // тут реальная отправка письма
        System.out.println("Отправляем письмо на " + email);
    }
}

В реальном проекте мы не хотим, чтобы при каждом запуске теста реально отправлялось письмо. Нам нужно просто проверить, что метод был вызван, или заглушить его выполнение, или заставить его выбросить исключение (чтобы протестировать обработку ошибок).

Для этого Mockito предоставляет специальный синтаксис с использованием doNothing(), doThrow(), doAnswer() и doCallRealMethod().

1. Самый частый случай: doNothing() — просто заглушить void метод

Если вам нужно, чтобы void метод ничего не делал при вызове — используйте doNothing(). Это стандартное поведение Mockito по умолчанию для void методов, но иногда полезно указать явно (например, для читаемости или при использовании spy).

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class EmailServiceTest {

    @Mock
    private EmailService emailService;

    @Test
    void testDoNothing() {
        // Настраиваем: при вызове sendWelcomeEmail ничего не делать
        doNothing().when(emailService).sendWelcomeEmail(anyString());

        // Вызываем метод
        emailService.sendWelcomeEmail("test@example.com");

        // Проверяем, что метод действительно был вызван
        verify(emailService).sendWelcomeEmail("test@example.com");
    }
}

Важно: Если вы используете @Mock, то doNothing() можно не писать — Mockito по умолчанию делает void методы "пустышками". Но если вы используете @Spy (шпион), то реальный метод будет вызван, и doNothing() нужен, чтобы это предотвратить.

2. doThrow() — симулируем ошибку в void методе

Очень частая задача: проверить, как ваш код реагирует, если сервис отправки писем упал с ошибкой. Для этого мы заставляем void метод выбросить исключение.

@Test
void testDoThrow() {
    // Настраиваем: при вызове sendWelcomeEmail выбросить RuntimeException
    doThrow(new RuntimeException("Ошибка отправки письма"))
            .when(emailService).sendWelcomeEmail(anyString());

    // Теперь при вызове метода будет исключение
    assertThrows(RuntimeException.class, () -> {
        emailService.sendWelcomeEmail("fail@example.com");
    });

    // Проверяем, что метод был вызван (и упал)
    verify(emailService).sendWelcomeEmail("fail@example.com");
}

Этот подход идеален для тестирования блоков try-catch или когда нужно проверить, что программа корректно обрабатывает ошибки.

3. doAnswer() — выполняем кастомную логику при вызове void метода

Иногда нужно не просто заглушить метод, а выполнить какую-то логику: например, записать аргумент в список или изменить состояние другого объекта.

@Test
void testDoAnswer() {
    // Создаём список для записи аргументов
    final List<String> capturedEmails = new ArrayList<>();

    // Настраиваем: при вызове sendWelcomeEmail добавляем email в список
    doAnswer(invocation -> {
        String email = invocation.getArgument(0);
        capturedEmails.add(email);
        return null; // void метод должен вернуть null
    }).when(emailService).sendWelcomeEmail(anyString());

    // Вызываем метод
    emailService.sendWelcomeEmail("alice@example.com");
    emailService.sendWelcomeEmail("bob@example.com");

    // Проверяем, что оба email были записаны
    assertEquals(2, capturedEmails.size());
    assertTrue(capturedEmails.contains("alice@example.com"));
    assertTrue(capturedEmails.contains("bob@example.com"));
}

Когда это полезно? Например, если вы тестируете кэш, и при вызове void метода нужно имитировать сохранение данных в кэш-слой.

4. doCallRealMethod() — вызываем реальный метод (для spy)

Используется, когда у вас есть @Spy (шпион) — объект, у которого по умолчанию вызываются реальные методы, но некоторые вы хотите переопределить. doCallRealMethod() явно говорит: "вызови реальный метод".

import org.mockito.Spy;

@ExtendWith(MockitoExtension.class)
public class SpyTest {

    @Spy
    private EmailService emailService = new EmailService();

    @Test
    void testDoCallRealMethod() {
        // По умолчанию для spy вызывается реальный метод.
        // Но если мы хотим явно указать это (например, для читаемости):
        doCallRealMethod().when(emailService).sendWelcomeEmail(anyString());

        // Вызовем реальный метод (он напечатает в консоль)
        emailService.sendWelcomeEmail("real@example.com");

        // Проверяем, что он был вызван
        verify(emailService).sendWelcomeEmail("real@example.com");
    }
}

На практике doCallRealMethod() используется редко — обычно вы либо полностью мокаете метод, либо оставляете реальный по умолчанию.

5. verify() — как проверить, что void метод был вызван

Даже если вы не мокаете поведение void метода, вы можете (и должны) проверить, что он был вызван с правильными аргументами. Для этого используется verify().

@Test
void testVerifyVoidMethod() {
    // Просто вызываем метод (без мока — реальный объект)
    // Но обычно у вас mock или spy

    // Вызываем
    emailService.sendWelcomeEmail("verify@example.com");

    // Проверяем количество вызовов
    verify(emailService, times(1)).sendWelcomeEmail("verify@example.com");

    // Проверяем, что метод не вызывался с другим ар                

Блоги

Рекомендаций по книгам