Fans simulados y espías

Al ejecutar pruebas, a veces es necesario simular ciertos componentes en el contexto de la aplicación. Por ejemplo, es posible que tenga una fachada para algún servicio remoto al que no se pueda acceder durante el desarrollo. La burla también puede ser útil si necesita simular fallas que son difíciles de causar en un entorno real.

Spring Boot contiene una anotación @MockBean que se puede usar para definir un Mockito. simulacro de un bean dentro de ApplicationContext. Puede utilizar una anotación para agregar nuevos beans o reemplazar una definición de bean existente. La anotación se puede utilizar directamente en clases de prueba, en campos dentro de una prueba o en clases y campos con la anotación @Configuration. Cuando se usa con un campo, también se inyecta una instancia del objeto simulado creado. Los simulados beans se restablecen automáticamente después de ejecutar cada método de prueba.

Si la prueba utiliza una de las anotaciones de prueba de Spring Boot (por ejemplo, @ SpringBootTest), esta característica se habilita automáticamente. Para utilizar esta característica con otro tipo de organización, debe agregar explícitamente oyentes, como se muestra en el siguiente ejemplo:

Java
importar org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; importar org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener; importar org.springframework.test.context.ContextConfiguration; importar org.springframework.test.context.TestExecutionListeners; 
                @ContextConfiguration(classes = MyConfig.class) @TestExecutionListeners({ MockitoTestExecutionListener.class, ResetMocksTestExecutionListener.class }) class MyTests { // ... } 
Kotlin
importar org.springframework. boot.test.mock.mockito.MockitoTestExecutionListener import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestExecutionListeners @ContextConfiguration(classes = [MyConfig::class]) @TestExecutionListeners( MockitoTestExecutionListener::class, ResetMocksTestExecutionListener::class ) class MyTests { // ... } 

El siguiente ejemplo reemplaza un bean RemoteService existente con una implementación simulada:

Java
import org.junit.jupiter.api.Test; importar org.springframework.beans.factory.annotation.Autowired; importar org.springframework.boot.test.context.SpringBootTest; importar org.springframework.boot.test.mock.mockito.MockBean; importar estática org.assertj.core.api.Assertions.assertThat; importar org.mockito.BDDMockito.given estático; @SpringBootTest class MyTests { @Autowired private Reverser inversor; @MockBean servicio remoto privado servicio remoto; @Test void exampleTest() { dado(this.remoteService.getValue()).willReturn("primavera"); Cadena inversa = this.reverser.getReverseValue(); // Llamadas inyectadas RemoteService afirmarThat(reverse).isEqualTo("gnirps"); } } 
Kotlin
importar org.assertj.core.api.Assertions.assertThat importar org.junit.jupiter.api.Test importar org.mockito.BDDMockito.given importar org.springframework.beans.factory.annotation.Autowired importar org.springframework.boot .test.context.SpringBootTest import org.springframework.boot.test.mock.mockito.MockBean @SpringBootTest class MyTests(@Autowired val reversible: Inversor, @MockBean val servicio remoto : RemoteService) { @Test fun exampleTest() { dado(remoteService.value).willReturn("spring") val reversa = reversible.reverseValue // Llamadas inyectadas RemoteService afirmarQue(reverse).isEqualTo("gnirps") } } 
-La anotación @MockBean no se puede utilizar para Lógica simulada: el bean que se ejecuta cuando se actualiza el contexto de la aplicación. Para cuando se ejecute la prueba, la actualización del contexto de la aplicación ya se habrá completado y será demasiado tarde para configurar la lógica operativa simulada. En esta situación, recomendamos utilizar un método marcado con la anotación @Bean para crear y configurar el objeto simulado.

Alternativamente, puede utilizar anotación @SpyBean para envolver cualquier bean existente con un objeto spy de Mockito.

Proxies de CGLib, como los creados para los beans incluidos en el ámbito de accesibilidad, declare los métodos proxy como final. Esto impide que el marco Mockito funcione correctamente porque no puede simular ni seguir los métodos final en su configuración predeterminada. Si necesita simular o monitorear dicho bean, configure Mockito para usar un constructor de objetos simulados en línea agregando org.mockito:mockito-inline a las dependencias de prueba de su aplicación. Esto permite a Mockito burlarse y seguir los métodos final.
Aunque el marco de pruebas de Spring almacena en caché los contextos de las aplicaciones entre pruebas y reutiliza los contexto para pruebas con la misma configuración, el uso de las anotaciones @MockBean o @SpyBean afecta la clave de caché, lo que probablemente aumentará la cantidad de contextos.
Si usa la anotación @SpyBean para realizar un seguimiento de los beans con métodos anotados @Cacheable que hacen referencia a parámetros por nombre , su aplicación debe compilarse con la opción -parameters. De esta manera, se garantiza que la infraestructura de almacenamiento en caché tendrá acceso a los nombres de los parámetros una vez que se descubra el bean.
Si la anotación es @SpyBean se usa para monitorear el bean que está siendo proxy en Spring, en algunas situaciones puede ser necesario eliminar el proxy de Spring, por ejemplo, al configurar eventos esperados usando given o when. Esto se hace usando AopTestUtils.getTargetObject(yourProxiedSpy).

Pruebas de configuración automática

El sistema de configuración automática de Spring Boot funciona muy bien con las aplicaciones, pero a veces puede ser demasiado redundante para las pruebas. A menudo resulta práctico cargar sólo aquellas partes de la configuración que son necesarias para probar la "capa" de la aplicación. Por ejemplo, es posible que desee asegurarse de que los controladores Spring MVC representen las URL correctamente, pero no desee involucrar llamadas a bases de datos en esas pruebas, o tal vez desee probar entidades JPA, pero el nivel web no está interesado en ejecutar esas pruebas. . .

El módulo spring-boot-test-autoconfigure contiene una serie de anotaciones que se pueden utilizar para configurar automáticamente dichas "capas". Cada uno de ellos funciona de la misma manera, representando una anotación del formulario @...​Test, que carga ApplicationContext, y una o más anotaciones del formulario @AutoConfigure...​ , que se puede utilizar para configurar parámetros de configuración automática.

Cada capa restringe el escaneo de componentes al área relevante componentes y carga un conjunto muy limitado de clases de configuración automática. Si necesita excluir uno de ellos, la mayoría de las anotaciones del formulario @...​Test proporcionan un atributo excludeAutoConfiguration. Como alternativa, puede utilizar la anotación @ImportAutoConfiguration#exclude.
Habilitar múltiples “capas” usando múltiples anotaciones de la forma @…​Test en una prueba no es compatible. Si se requieren varias "capas", seleccione una de las anotaciones @...​Test y agregue las anotaciones @AutoConfigure...​ a las otras "capas" manualmente.
También puedes usar anotaciones como @AutoConfigure...​ con la anotación estándar @SpringBootTest. Puede utilizar esta combinación si no está interesado en crear capas de su aplicación, pero necesita algunos de los beans de prueba configurados automáticamente.

Pruebas JSON configuradas automáticamente

Para garantizar Para que los objetos de serialización y deserialización JSON funcionen como se esperaba, puede utilizar la anotación @JsonTest. La anotación @JsonTest configura automáticamente un asignador JSON compatible disponible, que puede ser una de las siguientes bibliotecas:

  • ObjectMapper de Jackson, cualquier beans con la anotación @JsonComponent y cualquier Módulos de Jackson

  • Gson

  • Jsonb

La lista de configuraciones automáticas activadas por la anotación @JsonTest se puede encontrar en el apéndice de documentación.

Si necesita configurar elementos de configuración automática, puede utilizar la anotación @AutoConfigureJsonTesters.

Spring Boot contiene clases auxiliares basadas en AssertJ que funcionan con JSONAssert y bibliotecas JsonPath, diseñadas para comprobar que la asignación JSON prevista es correcta. Las clases JacksonTester, GsonTester, JsonbTester y BasicJsonTester se pueden utilizar para las bibliotecas Jackson, Gson, Jsonb y Strings. , respectivamente. Cualquier campo auxiliar de la clase de prueba se puede marcar con la anotación @Autowired cuando se utiliza la anotación @JsonTest. El siguiente ejemplo muestra una clase de prueba para la biblioteca Jackson:

Java
importar org.junit .jupiter .api.Prueba; importar org.springframework.beans.factory.annotation.Autowired; importar org.springframework.boot.test.autoconfigure.json.JsonTest; importar org.springframework.boot.test.json.JacksonTester; importar estática org.assertj.core.api.Assertions.assertThat; @JsonTest class MyJsonTests { @Autowired private JacksonTester<VehicleDetails> json; @Test void serialize() lanza una excepción {Detalles del vehículo = nuevos Detalles del vehículo("Honda", "Civic"); // Aserción para un archivo ".json" en el mismo paquete que la prueba afirmarThat(this.json.write(details)).isEqualToJson("expected.json"); // O usar aserciones basadas en la ruta JSON afirmarThat(this.json.write(details)).hasJsonPathStringValue("@.make"); afirmarQue(this.json.write(detalles)).extractingJsonPathStringValue("@.make").isEqualTo("Honda"); } @Test void deserialize() lanza una excepción { String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"; afirmarQue(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus")); afirmarQue(this.json.parseObject(content).getMake()).isEqualTo("Ford"); } } 
Kotlin
importar org.assertj.core.api.Assertions.assertThat importar org.junit.jupiter.api.Test importar org.springframework.beans.factory.annotation.Autowired importar org.springframework.boot.test.autoconfigure.json.JsonTest importar org.springframework.boot.test.json.JacksonTester @JsonTest clase MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) { @Test fun serialize() { val detalles = VehicleDetails("Honda", "Civic") // Aserción para un archivo ".json" en el mismo paquete que la prueba afirmarThat(json.write(details)).isEqualToJson("expected.json") // O use aserciones basadas en la ruta JSON afirmarThat(json.write(details)).hasJsonPathStringValue("@.make") afirmarThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda") } @Test fun deserialize() { val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}" afirmarThat(json.parse(content)).isEqualTo( VehicleDetails ("Ford", "Focus")) afirmarThat(json.parseObject(content).make).isEqualTo("Ford") } } 
Las clases auxiliares JSON también se pueden usar directamente en pruebas unitarias estándar. Para hacer esto, llame al método initFields de la clase auxiliar en su método anotado con @Before si no se utiliza la anotación @JsonTest.

Si usa las clases auxiliares Spring Boot basadas en AssertJ para agregar una aserción para un valor numérico en una ruta JSON determinada, es posible que no necesite usar isEqualTo dependiendo del tipo. En su lugar, puede utilizar la función satisfies en AssertJ para agregar una afirmación de que un valor satisface una condición determinada. Por ejemplo, el siguiente ejemplo agrega una declaración de que el número real es un valor flotante cercano a 0.15 dentro del desplazamiento 0.01.

Java
@Test void someTest() lanza una excepción { SomeObject value = new SomeObject(0.152f); afirmarQue(this.json.write(valor)).extractingJsonPathNumberValue("@.test.numberValue") .satisfies((número) -> afirmarQue(número.floatValue()).isCloseTo(0.15f, dentro de(0.01f) )); } 
Kotlin
@Test fun someTest() { val value = SomeObject(0.152f) afirmarQue(json .write(value)).extractingJsonPathNumberValue("@.test.numberValue") .satisfies(ThrowingConsumer { number -> afirmarThat(number.toFloat()).isCloseTo(0.15f, inside(0.01f)) }) } 

Pruebas autoconfigurables de Spring MVC

Para probar los controladores Spring MVC para la funcionalidad prevista, utilice @WebMvcTest anotación. La anotación @WebMvcTest configura automáticamente el marco Spring MVC y limita los beans escaneados a @Controller, @ControllerAdvice, @JsonComponent. >, Convertidor, GenericConverter, Filtro, HandlerInterceptor, WebMvcConfigurer, WebMvcRegistrations y HandlerMethodArgumentResolver. Los beans normales con anotaciones @Component y @ConfigurationProperties no se analizan cuando se utiliza la anotación @WebMvcTest. Para agregar beans marcados con la anotación @ConfigurationProperties, puede usar la anotación @EnableConfigurationProperties.

Si necesita registrar componentes adicionales, como Module de Jackson, puede importar clases de configuración adicionales utilizando la anotación @Import en su prueba.

A menudo, la anotación @WebMvcTest se limita a un único controlador y se usa junto con la anotación @MockBean para pasar implementaciones simuladas para los objetos de comunicación requeridos.

@WebMvcTest también configura automáticamente MockMvc. Mock MVC proporciona una manera eficiente de probar rápidamente controladores MVC sin tener que ejecutar un servidor HTTP completo.

También puede configurar automáticamente MockMvc sin la anotación @WebMvcTest (y por ejemplo, con la anotación @SpringBootTest), anotándolo con @AutoConfigureMockMvc. El siguiente ejemplo utiliza MockMvc:
Java
importar org. junit.jupiter.api.Prueba; importar org.springframework.beans.factory.annotation.Autowired; importar org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; importar org.springframework.boot.test.mock.mockito.MockBean; importar org.springframework.http.MediaType; importar org.springframework.test.web.servlet.MockMvc; importar org.mockito.BDDMockito.given estático; importar estática org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; importar estático org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; importar estática org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(UserVehicleController.class) class MyControllerTests { @Autowired private MockMvc mvc; @MockBean userVehicleService privado UserVehicleService; @Test void testExample() lanza una excepción { dada(this.userVehicleService.getVehicleDetails("sboot")) .willReturn(new VehicleDetails("Honda", "Civic")); this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN)) .andExpect(status().isOk()) .andExpect(content().string("Honda Civic")); } } 
Kotlin
importar org.junit.jupiter.api.Test importar org.mockito.BDDMockito.given importar org.springframework.beans.factory.annotation.Autowired importar org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest importar org .springframework.boot.test.mock.mockito.MockBean importar org.springframework.http.MediaType importar org.springframework.test.web.servlet.MockMvc importar org.springframework.test.web.servlet.request.MockMvcRequestBuilders importar org.springframework .test.web.servlet.result.MockMvcResultMatchers @WebMvcTest(UserVehicleController::class) class MyControllerTests(@Autowired val mvc: MockMvc) { @MockBean lateinit var userVehicleService: UserVehicleService @Test divertido testExample() { dado(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic")) mvc.perform(MockMvcRequestBuilders.get("/sboot/vehicle").accept( MediaType.TEXT_PLAIN)) .andExpect(MockMvcResultMatchers.status().isOk) .andExpect(MockMvcResultMatchers.content().string("Honda Civic")) } } 
Si necesita configurar elementos de configuración automática (por ejemplo, si necesita aplicar filtros de servlet), puede usar los atributos en el Anotación @AutoConfigureMockMvc.

Si está utilizando HtmlUnit y Selenium, la configuración automática también proporciona un bean WebClient para HtmlUnit y/o un WebDriver frijol para selenio. El siguiente ejemplo utiliza HtmlUnit:

Java
import com.gargoylesoftware.htmlunit.WebClient; importar com.gargoylesoftware.htmlunit.html.HtmlPage; importar org.junit.jupiter.api.Test; importar org.springframework.beans.factory.annotation.Autowired; importar org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; importar org.springframework.boot.test.mock.mockito.MockBean; importar estática org.assertj.core.api.Assertions.assertThat; importar org.mockito.BDDMockito.given estático; @WebMvcTest(UserVehicleController.class) class MyHtmlUnitTests { @Autowired WebClient privado webClient; @MockBean userVehicleService privado UserVehicleService; @Test void testExample() lanza una excepción { dada(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic")); Página HtmlPage = this.webClient.getPage("/sboot/vehicle.html"); afirmarQue(page.getBody().getTextContent()).isEqualTo("Honda Civic"); } } 
Kotlin
importar com.gargoylesoftware.htmlunit.WebClient importar com.gargoylesoftware.htmlunit.html.HtmlPage importar org.assertj.core.api.Assertions.assertThat importar org.junit.jupiter.api.Test importar org.mockito.BDDMockito.given importar org.springframework.beans.factory.annotation.Autowired importar org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest importar org.springframework.boot.test.mock.mockito.MockBean @WebMvcTest(UserVehicleController::class) class MyHtmlUnitTests(@Autowired val webClient: WebClient) { @MockBean lateinit var userVehicleService: UserVehicleService @Test fun testExample() { dado(userVehicleService.getVehicleDetails("sboot")) .willReturn(VehicleDetails("Honda", "Civic")) val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html") afirmarThat(page.body.textContent).isEqualTo("Honda Civic") } } 
De forma predeterminada, Spring Boots coloca WebDriver en un "área de disponibilidad" especial para garantizar que el controlador salga después de cada prueba y se implemente una nueva instancia. Si esta lógica operativa es innecesaria, puede agregar la anotación @Scope("singleton") a la definición de su WebDriver con el @Bean annotation.
Un alcance de accesibilidad webDriver creado por Spring Boot reemplazará cualquier alcance de accesibilidad definido por el usuario con el mismo nombre. Si tiene su propio alcance de accesibilidad webDriver definido, es posible que deje de funcionar cuando utilice la anotación @WebMvcTest.

Si el classpath tiene la anotación Spring Security @WebMvcTest también escaneará los beans WebSecurityConfigurer. En lugar de deshabilitar completamente la seguridad para dichas pruebas, puede utilizar el soporte de pruebas de Spring Security.

A veces, escribir pruebas en Spring MVC no es suficiente; Spring Boot puede ayudarlo a ejecutar pruebas de un extremo a otro con todas las funciones utilizando un servidor real.

Pruebas de autoconfiguración de Spring WebFlux

Para garantizar que los controladores Spring WebFlux funcionen como se espera, puede utilizar la anotación @WebFluxTest. La anotación @WebFluxTest configura automáticamente el marco Spring WebFlux y limita los beans escaneados a @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, WebFilter y WebFluxConfigurer. Los beans normales con anotaciones @Component y @ConfigurationProperties no se escanean cuando se utiliza la anotación @WebFluxTest. Para agregar beans marcados con la anotación @ConfigurationProperties, puede usar la anotación @EnableConfigurationProperties.

Si necesita registrar componentes adicionales como Module de la biblioteca Jackson, puede importar clases de configuración adicionales utilizando la anotación @Import en la prueba.

A menudo, la anotación de @WebFluxTest se limita a un único controlador y se utiliza junto con la anotación @MockBean para pasar implementaciones simuladas para los objetos de comunicación requeridos.

El código de anotación @WebFluxTest> también configura automáticamente WebTestClient, lo que proporciona un medio eficiente para probar rápidamente los controladores WebFlux sin la necesidad de ejecutar un servidor HTTP completo.

También puede configurar automáticamente WebTestClient sin la anotación @WebFluxTest (y por ejemplo con la anotación @SpringBootTest) anotándola con @AutoConfigureWebTestClient . El siguiente ejemplo muestra una clase que utiliza las anotaciones @WebFluxTest y WebTestClient:
Java
importar org.junit.jupiter.api.Test; importar org.springframework.beans.factory.annotation.Autowired; importar org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; importar org.springframework.boot.test.mock.mockito.MockBean; importar org.springframework.http.MediaType; importar org.springframework.test.web.reactive.server.WebTestClient; importar org.mockito.BDDMockito.given estático; 
            @WebFluxTest(UserVehicleController.class) clase MyControllerTests { @Autowired webTestClient privado webClient; @MockBean userVehicleService privado UserVehicleService; @Test void testExample() { dado(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic")); this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange() .expectStatus().isOk() .expectBody(String.class).isEqualTo("Honda Civic" ); } } 
Kotlin
importar org.junit.jupiter.api.Test importar org.mockito.BDDMockito.given importar org.springframework.beans.factory.annotation.Autowired importar org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest importar org .springframework.boot.test.mock.mockito.MockBean importar org.springframework.http.MediaType importar org.springframework.test.web.reactive.server.WebTestClient importar org.springframework.test.web.reactive.server.expectBody @WebFluxTest(UserVehicleController::class) class MyControllerTests(@Autowired val webClient: WebTestClient) { @MockBean lateinit var userVehicleService: UserVehicleService @Test fun testExample() { dado(userVehicleService.getVehicleDetails ("sboot")) .willReturn(VehicleDetails("Honda", "Civic")) webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange() .expectStatus() .isOk .expectBody<String>().isEqualTo("Honda Civic") } } 
Esta configuración solo es compatible con aplicaciones WebFlux, ya que el uso de WebTestClient en una aplicación web simulada actualmente solo funciona en WebFlux.
La anotación @WebFluxTest no puede detectar rutas registradas con el marco funcional web. Para probar los beans RouterFunction en contexto, considere importar RouterFunction usted mismo usando la anotación @Import o usando la anotación @SpringBootTest .
La anotación @WebFluxTest no puede detectar una configuración de seguridad personalizada registrada como @Bean escriba SecurityWebFilterChain. Para incluirlo en su prueba, debe importar la configuración que registra el bean usando la anotación @Import o @SpringBootTest annotation.
A veces escribir pruebas en Spring WebFlux no es suficiente; Spring Boot puede ayudarlo a ejecutar pruebas de un extremo a otro con todas las funciones utilizando un servidor real.