Mockito: продвинутые возможности — перехватчики аргументов и шпионы
Перехватывайте аргументы методов с помощью ArgumentCaptor и частично имитируйте реальные объекты с помощью Mockito.spy.
«Mockito: продвинутые возможности — перехватчики аргументов и шпионы» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
ArgumentCaptor: Перехват аргументов методов
ArgumentCaptor перехватывает аргументы, переданные в имитируемый метод, чтобы вы могли проверить их. Это особенно полезно, когда аргумент создается внутри проверяемой системы и недоступен напрямую.
Создание и использование ArgumentCaptor
Создайте объект-перехватчик для типа аргумента, передайте captor.capture() в качестве сопоставителя аргумента в verify(), а затем вызовите captor.getValue(), чтобы получить перехваченный аргумент.
@Test
void sends_correct_email() {
ArgumentCaptor<EmailMessage> captor = ArgumentCaptor.forClass(EmailMessage.class);
userService.register(new CreateUserRequest("alice@example.com"));
verify(emailService).send(captor.capture());
EmailMessage msg = captor.getValue();
assertEquals("alice@example.com", msg.getTo());
assertTrue(msg.getSubject().contains("Welcome"));
}Аннотация @Captor
Используйте @Captor вместе с @ExtendWith(MockitoExtension.class), чтобы не писать громоздкие вызовы ArgumentCaptor.forClass().
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock EmailService emailService;
@Captor ArgumentCaptor<EmailMessage> emailCaptor;
@Test
void captures_email() {
// ...
verify(emailService).send(emailCaptor.capture());
assertEquals("Welcome!", emailCaptor.getValue().getSubject());
}
}Перехват нескольких вызовов
Используйте captor.getAllValues(), если метод вызывается несколько раз. Метод возвращает список всех перехваченных значений в порядке вызовов.
verify(emailService, times(3)).send(emailCaptor.capture());
List<EmailMessage> messages = emailCaptor.getAllValues();
assertEquals(3, messages.size());
assertEquals("Confirmation", messages.get(0).getSubject());Mockito-шпионы: частичное имитирование
Mockito.spy() оборачивает реальный объект. Все методы работают как обычно, если явно не настроить их поведение. Используйте шпиона, когда нужно протестировать класс с несколькими сложными для проверки зависимостями, но в основном работающий правильно.
List<String> realList = new ArrayList<>();
List<String> spy = Mockito.spy(realList);
spy.add("hello"); // calls real ArrayList.add()
System.out.println(spy.size()); // 1 — real method called
doReturn(42).when(spy).size(); // stub just size()
System.out.println(spy.size()); // 42 — stubbedАннотация @Spy
Используйте @Spy вместе с @ExtendWith(MockitoExtension.class), чтобы создать шпионов. Поле должно быть инициализировано — непосредственно или с помощью @Spy UserService svc = new UserService(repo);.
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Spy
OrderService orderService = new OrderService(mockRepo, mockEmail);
}Шпионирование за компонентами Spring с помощью @SpyBean
В тестах Spring Boot @SpyBean оборачивает существующий компонент Spring в шпиона Mockito — сохраняется реальное поведение, если его не настроить.
@SpringBootTest
class IntegrationTest {
@SpyBean EmailService emailService;
@Test
void order_sends_email() {
orderService.placeOrder(cart);
verify(emailService).sendConfirmation(any());
}
}doReturn вместо when().thenReturn() для шпионов
Для шпионов используйте doReturn(value).when(spy).method() вместо when(spy.method()).thenReturn(value). Форма when(spy.method()) фактически вызывает реальный метод, что может привести к побочным эффектам.
// Correct for spies:
doReturn(List.of()).when(spy).findAll();
// WRONG for spies — calls real findAll() first:
when(spy.findAll()).thenReturn(List.of()); // real method called!Проверка отсутствия взаимодействий
Используйте verifyNoInteractions(mock), чтобы проверить, что имитируемый объект ни разу не вызывался, а после всех ожидаемых вызовов применяйте verifyNoMoreInteractions(mock), чтобы обнаружить неожиданные вызовы.
verifyNoInteractions(emailService); // never called
verify(repo).save(any());
verifyNoMoreInteractions(repo); // save() was the only interactionПроверка порядка вызовов с InOrder
Используйте InOrder, чтобы проверить, что методы имитируемого объекта вызывались в определенном порядке.
InOrder inOrder = inOrder(repo, emailService);
inOrder.verify(repo).save(any());
inOrder.verify(emailService).send(any()); // save happened BEFORE sendСопоставители аргументов
Сочетайте перехватчики с сопоставителями: any(), eq(), anyString(), argThat(predicate). Если для одного аргумента используется сопоставитель, сопоставители должны использоваться для всех аргументов.
verify(repo).save(argThat(user ->
user.getEmail().endsWith("@example.com") &&
user.getStatus() == UserStatus.ACTIVE));Быстрая проверка
Почему при настройке шпиона следует использовать doReturn вместо when().thenReturn()?
Итоги
ArgumentCaptor перехватывает аргументы для проверки после вызова. Шпионы оборачивают реальные объекты и делегируют им вызовы, если поведение не настроено. Для шпионов используйте doReturn. Для интеграционных тестов Spring используйте @SpyBean. Проверяйте порядок вызовов с помощью InOrder.
Часто задаваемые вопросы
Урок «Mockito: продвинутые возможности — перехватчики аргументов и шпионы» бесплатный?
Да — полный текст урока «Mockito: продвинутые возможности — перехватчики аргументов и шпионы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Mockito: продвинутые возможности — перехватчики аргументов и шпионы»?
Перехватывайте аргументы методов с помощью ArgumentCaptor и частично имитируйте реальные объекты с помощью Mockito.spy. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Mockito: продвинутые возможности — перехватчики аргументов и шпионы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Параметризованные тесты с @CsvSource и @MethodSource
- Mockito: продвинутые возможности — перехватчики аргументов и шпионы
- Срезы тестирования Spring Boot: @WebMvcTest и @DataJpaTest
- Testcontainers: интеграционные тесты с реальной базой данных