Usando Mockito ArgumentCaptor

1. Información general

En este tutorial, cubriremos un caso de uso común del uso de Mockito ArgumentCaptor en nuestras pruebas unitarias.

Alternativamente, para otros casos de uso de Mockito.verify , consulte nuestro Libro de recetas de Mockito Verify.

2. Usando ArgumentCaptor

ArgumentCaptor nos permite capturar un argumento pasado a un método para inspeccionarlo. Esto es especialmente útil cuando no podemos acceder al argumento fuera del método que nos gustaría probar.

Por ejemplo, considere una clase EmailService con un método de envío que nos gustaría probar:

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

En EmailService . send , observe cómo platform.deliver toma un nuevo correo electrónico como argumento. Como parte de nuestra prueba, nos gustaría comprobar que el campo de formato del nuevo correo electrónico esté configurado en Format.HTML . Para hacer esto, necesitamos capturar e inspeccionar el argumento que se pasa a platform.deliver .

Veamos cómo podemos usar ArgumentCaptor para ayudarnos.

2.1. Configurar la prueba unitaria

Primero, creemos nuestra clase de prueba unitaria:

@RunWith(MockitoJUnitRunner.class) public class EmailServiceUnitTest { @Mock DeliveryPlatform platform; @InjectMocks EmailService emailService; ... }

Estamos usando la anotación @Mock para simular DeliveryPlatform , que se inyecta automáticamente en nuestro EmailService con la anotación @InjectMocks . Consulte nuestro artículo de Anotaciones de Mockito para obtener más detalles.

2.2. Agregar un campo ArgumentCaptor

En segundo lugar, agreguemos un nuevo campo ArgumentCaptor de tipo Correo electrónico para almacenar nuestro argumento capturado:

@Captor ArgumentCaptor emailCaptor;

2.3. Capturar el argumento

En tercer lugar, usemos Mockito.verify con ArgumentCaptor para capturar el correo electrónico :

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

Luego podemos obtener el valor capturado y almacenarlo como un nuevo objeto de correo electrónico :

Email emailCaptorValue = emailCaptor.getValue();

2.4. Inspeccione el valor capturado

Finalmente, veamos la prueba completa con una aserción para inspeccionar el objeto de correo electrónico capturado :

@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. Evitar los golpes

Aunque podemos usar un ArgumentCaptor con stubbing , generalmente deberíamos evitar hacerlo. Para aclarar, en Mockito, esto generalmente significa evitar usar un ArgumentCaptor con Mockito . Cuando . Con stubbing, deberíamos usar un ArgumentMatcher en su lugar.

Veamos un par de razones por las que deberíamos evitar los trozos.

3.1. Disminución de la legibilidad de la prueba

Primero, considere una prueba simple :

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

Aquí, usamos Mockito.eq (credenciales) para especificar cuándo el simulacro debe devolver un objeto.

A continuación, considere la misma prueba usando un ArgumentCaptor en su lugar:

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

A diferencia de la primera prueba, observe cómo tenemos que realizar una aserción adicional en la última línea para hacer lo mismo que Mockito.eq (credenciales) .

Finalmente, observe cómo no está claro de inmediato a qué se refiere credentialsCaptor.capture () . Esto se debe a que tenemos que crear el captor fuera de la línea en la que lo usamos, lo que reduce la legibilidad.

3.2. Localización de defectos reducida

Otra razón es que si emailService.authenticatedSuccessfully no llama a platform.authenticate , obtendremos una excepción:

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

Esto se debe a que nuestro método stubped no ha capturado un argumento. Sin embargo, el problema real no está en nuestra prueba en sí, sino en el método real que estamos probando.

En otras palabras, nos desvía hacia una excepción en la prueba, mientras que el defecto real está en el método que estamos probando.

4. Conclusión

En este breve tutorial, analizamos un caso de uso general del uso de ArgumentCaptor . También analizamos las razones para evitar el uso de ArgumentCaptor con stubbing. Como de costumbre, todos nuestros ejemplos de código están disponibles en GitHub.