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:
import 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 {
// ...
}
import 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:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@SpringBootTest
class MyTests {
@Autowired
private Reverser reverser;
@MockBean
private RemoteService remoteService;
@Test
void exampleTest() {
given(this.remoteService.getValue()).willReturn("spring");
String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
@SpringBootTest
class MyTests(@Autowired val reverser: Reverser, @MockBean val remoteService: RemoteService) {
@Test
fun exampleTest() {
given(remoteService.value).willReturn("spring")
val reverse = reverser.reverseValue // Calls injected RemoteService
assertThat(reverse).isEqualTo("gnirps")
}
}
@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.
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.
@MockBean o
@SpyBean afecta la clave de caché, lo que probablemente aumentará la cantidad de contextos.
@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.
@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.
@...Test proporcionan un atributo
excludeAutoConfiguration. Como alternativa, puede utilizar la anotación
@ImportAutoConfiguration#exclude.
@…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.
@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:
ObjectMapperde Jackson, cualquier beans con la anotación@JsonComponenty cualquierModulesde JacksonGsonJsonb
@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:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.boot.test.json.JacksonTester;
import static org.assertj.core.api.Assertions.assertThat;
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
void serialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Aserción para un archivo ".json" file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// O usar aserciones basadas en la ruta JSON
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda");
}
@Test
void deserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.json.JsonTest
import org.springframework.boot.test.json.JacksonTester
@JsonTest
class MyJsonTests(@Autowired val json: JacksonTester<VehicleDetails>) {
@Test
fun serialize() {
val details = VehicleDetails("Honda", "Civic")
// Aserción para un archivo ".json" en el mismo paquete que la prueba
assertThat(json.write(details)).isEqualToJson("expected.json")
// O use aserciones basadas en la ruta JSON
assertThat(json.write(details)).hasJsonPathStringValue("@.make")
assertThat(json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda")
}
@Test
fun deserialize() {
val content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"
assertThat(json.parse(content)).isEqualTo(VehicleDetails("Ford", "Focus"))
assertThat(json.parseObject(content).make).isEqualTo("Ford")
}
}
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.
@Test
void someTest() throws Exception {
SomeObject value = new SomeObject(0.152f);
assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
}
@Test
fun someTest() {
val value = SomeObject(0.152f)
assertThat(json.write(value)).extractingJsonPathNumberValue("@.test.numberValue")
.satisfies(ThrowingConsumer { number ->
assertThat(number.toFloat()).isCloseTo(0.15f, within(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.
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.
MockMvc sin la anotación
@WebMvcTest (y por ejemplo, con la anotación
@SpringBootTest), anotándolo con
@AutoConfigureMockMvc. El siguiente ejemplo utiliza
MockMvc:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(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"));
}
}
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
@WebMvcTest(UserVehicleController::class)
class MyControllerTests(@Autowired val mvc: MockMvc) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(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"))
}
}
@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:
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
@WebMvcTest(UserVehicleController::class)
class MyHtmlUnitTests(@Autowired val webClient: WebClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot")).willReturn(VehicleDetails("Honda", "Civic"))
val page = webClient.getPage<HtmlPage>("/sboot/vehicle.html")
assertThat(page.body.textContent).isEqualTo("Honda Civic")
}
}
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.
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.
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.
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.
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:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.mockito.BDDMockito.given;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() {
given(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");
}
}
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@WebFluxTest(UserVehicleController::class)
class MyControllerTests(@Autowired val webClient: WebTestClient) {
@MockBean
lateinit var userVehicleService: UserVehicleService
@Test
fun testExample() {
given(userVehicleService.getVehicleDetails("sboot"))
.willReturn(VehicleDetails("Honda", "Civic"))
webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Honda Civic")
}
}
WebTestClient en una aplicación web simulada actualmente solo funciona en WebFlux.
@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 .
@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.
GO TO FULL VERSION