이 튜토리얼에서는 단위 테스트에서 Mockito ArgumentCaptor를 사용하는 일반적인 사용 사례를 다룹니다. 또는 다른 Mockito.verify 사용 사례에 대해서는 Mockito 확인 설명서를 참조하세요.
ArgumentCaptorArgumentCaptor를 사용하면 메서드에 전달된 인수를 캡처하여 검사할 수 있습니다. 이는 테스트하려는 메서드 외부의 인수에 액세스할 수 없을 때 특히 유용합니다.
예를 들어 테스트하려는 send 메서드가 있는 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);
}
...
}
EmailService.send에서 platform.deliver가 새 Email을 인수로 사용하는 방법을 확인하세요. 테스트의 일환으로 새 Email의 형식 필드가 Format.HTML로 설정되어 있는지 확인하고 싶습니다. 이를 위해서는 platform.deliver에 전달된 인수를 캡처하고 검사해야 합니다.
ArgumentCaptor를 사용하여 어떻게 도움을 줄 수 있는지 살펴보겠습니다.
먼저 단위 테스트 클래스를 만들어 보겠습니다.
@ExtendWith(MockitoExtension.class)
class EmailServiceUnitTest {
@Mock
DeliveryPlatform platform;
@InjectMocks
EmailService emailService;
...
}
우리는 @InjectMocks 어노테이션과 함께 EmailService에 자동으로 주입되는 DeliveryPlatform을 모의하기 위해 @Mock 어노테이션을 사용하고 있습니다. 자세한 내용은 Mockito 어노테이션 기사를 참조하세요.
ArgumentCaptor Field둘째, 캡처된 인수를 저장하기 위해 Email 유형의 새 ArgumentCaptor 필드를 추가하겠습니다.
@Captor
ArgumentCaptor<Email> emailCaptor;
셋째, ArgumentCaptor와 함께 verify()를 사용하여 Email을 캡처해 보겠습니다.
verify(platform).deliver(emailCaptor.capture());
그런 다음 캡처된 값을 가져와 새 Email 개체로 저장할 수 있습니다.
Email emailCaptorValue = emailCaptor.getValue();
마지막으로 캡처된 Email 개체를 assert하기 위한 어설션이 포함된 전체 테스트를 살펴보겠습니다.
@Test
void whenDoesSupportHtml_expectHTMLEmailFormat() {
String to = "info@baeldung.com";
String subject = "Using ArgumentCaptor";
String body = "Hey, let'use ArgumentCaptor";
emailService.send(to, subject, body, true);
verify(platform).deliver(emailCaptor.capture());
Email value = emailCaptor.getValue();
assertThat(value.getFormat()).isEqualTo(Format.HTML);
}
스텁과 함께 ArgumentCaptor를 사용할 수 있지만 일반적으로 그렇게 하는 것은 피해야 합니다. 명확히 하자면, Mockito에서 이는 일반적으로 Mockito.when과 함께 ArgumentCaptor를 사용하지 않는 것을 의미합니다. 스텁을 사용하면 ArgumentMatcher를 대신 사용해야 합니다.
스터빙을 피해야 하는 몇 가지 이유를 살펴보겠습니다.
먼저 간단한 테스트를 고려해보세요
Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
when(platform.authenticate(eq(credentials))).thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
여기서는 모의 객체가 객체를 반환해야 하는 시기를 지정하기 위해 eq(credentials)를 사용합니다. 다음으로, 대신 ArgumentCaptor를 사용하여 동일한 테스트를 고려해 보세요.
Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
when(platform.authenticate(credentialsCaptor.capture())).thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
assertEquals(credentials, credentialsCaptor.getValue());
첫 번째 테스트와 달리 eq(credentials)와 동일한 작업을 수행하기 위해 마지막 줄에서 추가 어설션을 수행해야 하는 방법에 유의하세요.
마지막으로, credentialCaptor.capture()가 무엇을 참조하는지 즉시 명확하지 않다는 점에 유의하세요. 이는 우리가 사용하는 라인 외부에 captor를 생성해야 하므로 가독성이 떨어지기 때문입니다.
emailService.authenticatedSuccessfully이 platform.authenticate를 호출하지 않으면 예외가 발생하기 때문입니다.
org.mockito.exceptions.base.MockitoException:
No argument value was captured!
이는 우리의 스텁 메서드가 인수를 캡처하지 않았기 때문입니다. 그러나 실제 문제는 테스트 자체가 아니라 테스트하고 있는 실제 방법에 있습니다.
즉, 실제 결함은 우리가 테스트하는 방법에 있는 반면 테스트에서는 예외가 발생하도록 잘못 안내합니다.