1. Обзор
В этом руководстве мы рассмотрим распространенный вариант использования Mockito ArgumentCaptor
в наших модульных тестах.
Кроме того, для других вариантов использования Mockito.verify см. нашу
кулинарную книгу Mockito Verify .
2. Использование ArgumentCaptor
ArgumentCaptor
позволяет нам захватить аргумент, переданный методу, для его проверки. Это особенно полезно, когда мы не можем получить доступ к аргументу за пределами метода, который мы хотим протестировать.
Например, рассмотрим класс EmailService
с методом отправки
, который мы хотели бы протестировать:
public class EmailService {
private DeliveryPlatform platform;
public EmailService(DeliveryPlatform platform) {
this.platform = platform;
}
public void send(String to, String subject, String body, boolean html) {
Format format = Format.TEXT_ONLY;
if (html) {
format = Format.HTML;
}
Email email = new Email(to, subject, body);
email.setFormat(format);
platform.deliver(email);
}
...
}
В сервисе электронной почты
. send
, обратите внимание, как platform.deliver
принимает новое электронное
письмо в качестве аргумента. В рамках нашего теста мы хотели бы убедиться, что в поле формата нового сообщения электронной почты
установлено значение Format.HTML
. Для этого нам нужно захватить и проверить аргумент, который передается в platform.deliver
.
Давайте посмотрим, как мы можем использовать ArgumentCaptor
, чтобы помочь нам.
2.1. Настройте модульный тест
Во-первых, давайте создадим наш класс модульного теста:
@RunWith(MockitoJUnitRunner.class)
public class EmailServiceUnitTest {
@Mock
DeliveryPlatform platform;
@InjectMocks
EmailService emailService;
...
}
Мы используем аннотацию @Mock для имитации
DeliveryPlatform
, которая автоматически внедряется в наш EmailService
с аннотацией @InjectMocks
. Дополнительные сведения см. в нашей статье с аннотациями Mockito .
2.2. Добавьте поле ArgumentCaptor
Во-вторых, давайте добавим новое поле ArgumentCaptor
типа Email
для хранения захваченного аргумента:
@Captor
ArgumentCaptor<Email> emailCaptor;
2.3. Зафиксируйте аргумент
В-третьих, давайте используем Mockito.verify
с ArgumentCaptor
для захвата электронной почты
:
Mockito.verify(platform).deliver(emailCaptor.capture());
Затем мы можем получить захваченное значение и сохранить его как новый объект электронной почты
:
Email emailCaptorValue = emailCaptor.getValue();
2.4. Осмотрите захваченное значение
Наконец, давайте посмотрим весь тест с утверждением для проверки захваченного объекта электронной почты :
@Test
public void whenDoesSupportHtml_expectHTMLEmailFormat() {
String to = "info@foreach.com";
String subject = "Using ArgumentCaptor";
String body = "Hey, let'use ArgumentCaptor";
emailService.send(to, subject, body, true);
Mockito.verify(platform).deliver(emailCaptor.capture());
Email value = emailCaptor.getValue();
assertEquals(Format.HTML, value.getFormat());
}
3. Избегайте заглушек
Хотя мы можем использовать ArgumentCaptor
с заглушками, обычно этого следует избегать. Чтобы уточнить, в Mockito это обычно означает отказ от использования ArgumentCaptor
с Mockito.when
. Вместо этого с заглушкой мы должны использовать ArgumentMatcher
.
Давайте рассмотрим несколько причин, по которым нам следует избегать заглушек.
3.1. Снижение читабельности теста
Сначала рассмотрим простой тест:
Credentials credentials = new Credentials("foreach", "correct_password", "correct_key");
Mockito.when(platform.authenticate(Mockito.eq(credentials)))
.thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
Здесь мы используем Mockito.eq(credentials)
, чтобы указать, когда макет должен возвращать объект.
Далее рассмотрим тот же тест с использованием ArgumentCaptor
:
Credentials credentials = new Credentials("foreach", "correct_password", "correct_key");
Mockito.when(platform.authenticate(credentialsCaptor.capture()))
.thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
assertEquals(credentials, credentialsCaptor.getValue());
В отличие от первого теста, обратите внимание, как мы должны выполнить дополнительное утверждение в последней строке, чтобы сделать то же самое, что и Mockito.eq(credentials)
.
Наконец, обратите внимание, что не сразу становится понятно, на что ссылается учетная запись Captor.capture()
. Это связано с тем, что мы должны создать захват за пределами строки, в которой мы его используем, что снижает читабельность.
3.2. Уменьшенная локализация дефектов
Другая причина заключается в том, что если emailService.authenticatedSuccessfully
не вызовет platform.authenticate
, мы получим исключение:
org.mockito.exceptions.base.MockitoException:
No argument value was captured!
Это потому, что наш заглушенный метод не захватил аргумент. Однако реальная проблема заключается не в самом тесте, а в методе, который мы тестируем.
Другими словами, это приводит нас к исключению в тесте, тогда как фактический дефект находится в тестируемом методе.
4. Вывод
В этой короткой статье мы рассмотрели общий вариант использования ArgumentCaptor
. Мы также рассмотрели причины, по которым следует избегать использования ArgumentCaptor
с заглушками.
Как обычно, все наши примеры кода доступны на GitHub .