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:
ObjectMapper
de Jackson, cualquier beans con la anotación@JsonComponent
y cualquierModules
de JacksonGson
Jsonb
@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