Рубрики
Без рубрики

Использование Mockito ArgumentCaptor

В этом уроке мы рассмотрим распространенный случай использования Mockito ArgumentCaptor в наших модульных тестах.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим распространенный случай использования Mockito ArgumentCaptor в наших модульных тестах.

Кроме того, для других вариантов использования Mockito.verify см. нашу кулинарную книгу Mockito Verify.

2. Использование ArgumentCaptor

ArgumentCaptor позволяет нам захватить аргумент, переданный методу, чтобы проверить его. Это особенно полезно, когда мы не можем получить доступ к аргументу вне метода, который мы хотели бы протестировать.

Например, рассмотрим Службу электронной почты класс с методом send , который мы хотели бы протестировать:

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);
    }

    ...
}

В Почтовой службе . отправить , обратите внимание, как platform.delivery принимает новый Email в качестве аргумента. В рамках нашего теста мы хотели бы проверить, что поле формата нового Email имеет значение 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 emailCaptor;

2.3. Зафиксируйте аргумент

В-третьих, давайте использовать Mockito.verify с помощью ArgumentCaptor для захвата электронной почты :

Mockito.verify(platform).deliver(emailCaptor.capture());

Затем мы можем получить захваченное значение и сохранить его как новый объект Email :

Email emailCaptorValue = emailCaptor.getValue();

2.4. Проверьте захваченное значение

Наконец, давайте посмотрим весь тест с утверждением для проверки захваченного объекта Email :

@Test
public void whenDoesSupportHtml_expectHTMLEmailFormat() {
    String to = "[email protected]";
    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 с stubbing , мы обычно должны избегать этого. Чтобы уточнить, в Mockito это обычно означает, что следует избегать использования ArgumentCaptor с Mockito.когда . При стаббинге вместо этого мы должны использовать ArgumentMatcher .

Давайте рассмотрим несколько причин, по которым мы должны избегать ударов.

3.1. Снижение Читаемости Теста

Во-первых, рассмотрим простой тест :

Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
Mockito.when(platform.authenticate(Mockito.eq(credentials)))
  .thenReturn(AuthenticationStatus.AUTHENTICATED);

assertTrue(emailService.authenticatedSuccessfully(credentials));

Здесь мы используем Mockito.eq(учетные данные) , чтобы указать, когда макет должен возвращать объект.

Затем рассмотрим тот же тест с использованием ArgumentCaptor вместо этого:

Credentials credentials = new Credentials("baeldung", "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.authenticated успешно не вызовет platform.authenticate , мы получим исключение:

org.mockito.exceptions.base.MockitoException: 
No argument value was captured!

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

Другими словами, он неверно направляет нас на исключение в тесте, в то время как фактический дефект находится в методе, который мы тестируем.

4. Заключение

В этом коротком уроке мы рассмотрели общий случай использования ArgumentCaptor . Мы также рассмотрели причины, по которым следует избегать использования ArgumentCaptor со стаббингом. Как обычно, все наши примеры кода доступны на GitHub .