Spring Boot proporciona una serie de utilidades y anotaciones que facilitan la prueba de su aplicación. El soporte de pruebas lo proporcionan dos módulos: spring-boot-test
contiene los elementos principales y spring-boot-test-autoconfigure
admite la configuración automática para pruebas.
La mayoría de los desarrolladores utilizan el spring-boot-starter-test
"inicio", que importa ambos módulos de prueba Spring Boot, así como JUnit Jupiter, AssertJ, Hamcrest y otras bibliotecas útiles.
Si tiene pruebas que usan JUnit 4, puede usar el motor antiguo JUnit 5 para ejecutarlas. Para usar el motor antiguo, agregue una dependencia de junit-vintage-engine
, como se muestra en el siguiente ejemplo:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
hamcrest-core
cayó en favor de org.hamcrest:hamcrest
, que forma parte de spring-boot-starter-test
.
Dependencias del área de prueba de disponibilidad
"starter" spring-boot-starter-test
(en test
scope
) contiene las siguientes bibliotecas proporcionadas:
JUnit 5: el estándar de facto para pruebas unitarias de aplicaciones Java.
Spring Test y Spring Boot Test: herramientas para admitir utilidades y pruebas de integración para aplicaciones Spring Boot.
AssertJ: Biblioteca de aserciones fluidas.
Hamcrest: Biblioteca de comparadores (también conocidos como restricciones o predicados).
Mockito: marco Java para burlarse)
JSONassert: biblioteca de aserciones JSON.
JsonPath: XPath para JSON.
Por lo general, estas bibliotecas comunes nos resultan útiles al escribir pruebas. Si estas bibliotecas no satisfacen sus necesidades, puede agregar dependencias de prueba adicionales según desee.
Prueba de aplicaciones Spring
Uno de los principales beneficios de la inyección de dependencias es que debería hacerlo Es más fácil modularizar las pruebas de código. Es posible crear instancias de objetos usando el operador new
sin siquiera involucrar a Spring. También puedes usar objetos simulados en lugar de dependencias reales.
A menudo quieres ir más allá de las pruebas unitarias y comenzar a integrar las pruebas (con ApplicationContext
de Spring). Es útil poder realizar pruebas de integración sin necesariamente implementar la aplicación o conectarse a otra infraestructura.
Spring Framework incluye un módulo de prueba especial para dichas pruebas de integración. Puede declarar una dependencia directamente en org.springframework:spring-test
o utilizar el spring-boot-starter-test
"iniciador" para una conexión transitiva.
Prueba de aplicaciones Spring Boot
Una aplicación Spring Boot es un ApplicationContext
para Spring, por lo que no se requiere nada especial para probarla aparte de lo que se hace para un contexto Spring estándar.
SpringApplication
para crearlo. .
Spring Boot proporciona una anotación @SpringBootTest
que se puede utilizar como alternativa a la anotación estándar @ContextConfiguration
para spring-test
cuando surge la necesidad en las funciones de Spring Boot. La anotación crea un ApplicationContext
usado en las pruebas a través de SpringApplication
. Además de la anotación @SpringBootTest
, también hay otras anotaciones para probar sectores más específicos de la aplicación.
@RunWith(SpringRunner.class)
a la prueba; de lo contrario, las anotaciones se ignorarán. Si está utilizando JUnit 5, no es necesario agregar el equivalente de la anotación
@ExtendWith(SpringExtension.class)
, ya que la anotación
@SpringBootTest
y la otra
@…Test
annotations ya están anotadas con él.
De forma predeterminada, la anotación @SpringBootTest
no inicia el servidor. Puede utilizar el atributo webEnvironment
para la anotación @SpringBootTest
para refinar aún más el progreso de sus pruebas:
MOCK
(predeterminado): carga el contexto webApplicationContext
y proporciona un objeto de entorno web simulado. Los servidores integrados no se inician cuando se utiliza esta anotación. Si el entorno web no está disponible en su classpath, este modo recurre de forma transparente a la creación de unApplicationContext
normal no web. Se puede utilizar en combinación con la anotación@AutoConfigureMockMvc
o@AutoConfigureWebTestClient
para realizar pruebas simuladas de una aplicación web.RANDOM_PORT
: cargaWebServerApplicationContext
y proporciona el entorno web real. Los servidores integrados inician y escuchan en un puerto aleatorio.DEFINED_PORT
: carga elWebServerApplicationContext
y proporciona el entorno web real. Los servidores integrados se inician y escuchan en el puerto especificado (desde el archivoapplication.properties
) o en el puerto predeterminado8080
.NONE
: cargaApplicationContext
usandoSpringApplication
, pero no proporciona ningún entorno web (simulado o no).
@Transactional
, de forma predeterminada se revierte la transacción al final de cada método de prueba. Sin embargo, dado que el uso de este esquema con
RANDOM_PORT
o
DEFINED_PORT
expone implícitamente el entorno de servlet real, el cliente y el servidor HTTP se ejecutan en subprocesos separados y, por lo tanto, en transacciones separadas. En este caso, cualquier transacción iniciada en el servidor no se revierte.
@SpringBootTest
con
webEnvironment = WebEnvironment .RANDOM_PORT
también inicia el servidor de administración en un puerto aleatorio separado si la aplicación usa un puerto diferente para el servidor de administración.
Definición del tipo de aplicación web
Si Spring MVC está disponible, el normal está configurado en el contexto de aplicación basado en MVC. Si solo está presente Spring WebFlux, esto se determinará y el contexto de la aplicación se configurará en función de WebFlux.
Si ambos marcos están presentes, Spring MVC tiene prioridad. Si desea probar una aplicación web reactiva en este escenario, debe establecer la propiedad spring.main.web-application-type
:
Definir una configuración de prueba
Si está familiarizado con Spring Test Framework, probablemente esté acostumbrado a usar @ContextConfiguration (classes=…)
para especificar qué @Configuration
para Spring debe cargarse. Alternativamente, a menudo puedes usar clases anidadas con la anotación @Configuration
en tu prueba.
Al probar aplicaciones Spring Boot, esto generalmente no es necesario. Las anotaciones @*Test
en Spring Boot buscan su configuración principal automáticamente si no ha definido una explícitamente.
El algoritmo de búsqueda comienza desde el paquete que contiene la prueba hasta que encuentra la clase, anotada con @SpringBootApplication
o @SpringBootConfiguration
. Siempre que haya estructurado su código adecuadamente, generalmente podrá encontrar la configuración básica.
Si está utilizando una anotación de prueba para probar una versión más capa específica de la aplicación, entonces debe evitar agregar opciones de configuración específicas del dominio en la clase de aplicación del método principal.
La configuración básica de escaneo de componentes, que incluye la anotación @SpringBootApplication
, define los filtros de exclusión que se utilizan para garantizar que la adquisición de sectores funcione como se esperaba. Si utiliza una directiva explícita con la anotación @ComponentScan
en una clase marcada con la anotación @SpringBootApplication
, tenga en cuenta que estos filtros se desactivarán. Si recurre a la obtención de sectores, debe definirlos nuevamente.
Si necesita configurar la configuración principal, puede usar una clase anidada marcada con @TestConfiguration
anotación. A diferencia de una clase anidada marcada con la anotación @Configuration
, que se usa en lugar de la configuración principal de la aplicación, se usa una clase anidada con la anotación @TestConfiguration
además de la configuración principal de la aplicación.
Excepción de configuración de prueba
Si la aplicación usa escaneo de componentes (por ejemplo, cuando usa las anotaciones @SpringBootApplication
o @ComponentScan
), es posible que encuentre que las clases de configuración de alto nivel que se crearon solo para ciertas pruebas se interceptan accidentalmente.
La anotación @TestConfiguration
se puede utilizar en una clase de prueba interna para configurar la configuración primaria. Cuando la anotación @TestConfiguration
marca una clase de alto nivel, especifica que las clases en src/test/java
no deben interceptarse durante el escaneo. Luego puede importar esta clase explícitamente cuando sea necesario, como se muestra en el siguiente ejemplo:
@ComponentScan
se usa directamente (es decir, no a través de la anotación
@SpringBootApplication
), debe registrar el
TypeExcludeFilter
con ella.
Uso de argumentos de la aplicación
Si su aplicación acepta argumentos, puede usar la anotación @SpringBootTest
para inyectarlos a través del atributo args
.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(args = "--app.test=one")
class MyApplicationArgumentTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(args = ["--app.test=one"])
class MyApplicationArgumentTests {
@Test
fun applicationArgumentsPopulated(@Autowired args: ApplicationArguments) {
assertThat(args.optionNames).containsOnly("app.test")
assertThat(args.getOptionValues("app.test")).containsOnly("one")
}
}
Pruebas con un entorno simulado
De forma predeterminada, la anotación @SpringBootTest
no inicia el servidor, sino que crea un entorno simulado. para probar puntos finales web.
En Spring MVC, puede consultar puntos finales web usando MockMvc
o WebTestClient
, como se muestra en el siguiente ejemplo:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.MockMvc;
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;
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
void testWithMockMvc(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
// Si Spring WebFlux está en el classpath, entonces puede administrar las pruebas MVC usando WebTestClient
@Test
void testWithWebTestClient(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
@SpringBootTest
@AutoConfigureMockMvc
class MyMockMvcTests {
@Test
fun testWithMockMvc(@Autowired mvc: MockMvc) {
mvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.status().isOk)
.andExpect(MockMvcResultMatchers.content().string("Hello World"))
}
// If Spring WebFlux está en el classpath, entonces puede administrar las pruebas MVC usando WebTestClient
@Test
fun testWithWebTestClient(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
ApplicationContext
completo, considere usar la anotación
@WebMvcTest
como alternativa.
Puede usar WebTestClient
con puntos finales de Spring WebFlux como se muestra en el siguiente ejemplo:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest
@AutoConfigureWebTestClient
class MyMockWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
Las pruebas en un entorno de simulación suelen ser más rápidas que cuando se utiliza un contenedor de servlet poblado. Sin embargo, debido a que la burla ocurre en el nivel Spring MVC, el código que se basa en la lógica del contenedor de servlet de nivel inferior no se puede probar directamente con MockMvc.
Por ejemplo, el manejo de errores de Spring Boot se basa en "páginas de error" proporcionadas por el contenedor de servlets. Esto significa que, si bien es posible verificar que la capa MVC genera y maneja excepciones según lo previsto, no es posible verificar directamente que se represente una página de error personalizada en particular. Si necesita probar estos matices de bajo nivel, puede ejecutar un servidor en pleno funcionamiento, como se describe en la siguiente sección.
Prueba con un servidor en ejecución
Si Si desea ejecutar un servidor en pleno funcionamiento, le recomendamos utilizar puertos aleatorios. Si usa @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
, el puerto disponible se elige aleatoriamente cada vez que se ejecuta la prueba.
El @LocalServerPort
Se puede utilizar la anotación para inyectar el puerto realmente utilizado en la prueba. Para mayor comodidad, las pruebas que necesitan realizar llamadas REST a un servidor en ejecución pueden vincular opcionalmente un WebTestClient
mediante la anotación @Autowire
, que permite referencias relativas al servidor en ejecución y tiene una API especial para probar respuestas, como se muestra en el siguiente ejemplo:
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.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello World");
}
}
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.context.SpringBootTest.WebEnvironment
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortWebTestClientTests {
@Test
fun exampleTest(@Autowired webClient: WebTestClient) {
webClient
.get().uri("/")
.exchange()
.expectStatus().isOk
.expectBody<String>().isEqualTo("Hello World")
}
}
WebTestClient
usted se puede utilizar tanto para servidores reales como para entornos simulados.
Esta configuración requiere que spring-webflux
esté presente en el classpath. Si no puede o no quiere agregar webflux, Spring Boot también brinda la posibilidad de usar TestRestTemplate
:
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.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
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.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.client.TestRestTemplate
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyRandomPortTestRestTemplateTests {
@Test
fun exampleTest(@Autowired restTemplate: TestRestTemplate) {
val body = restTemplate.getForObject("/", String::class.java)
assertThat(body).isEqualTo("Hello World")
}
}
Configuración de WebTestClient
Para configurar el bean WebTestClient
, configure el WebTestClientBuilderCustomizer
frijol. Cualquiera de estos beans se llama usando WebTestClient.Builder
, que se usa para crear WebTestClient
.
Usando JMX
Porque el marco de contexto de prueba almacena en caché el contexto, la tecnología JMX está deshabilitada de forma predeterminada para evitar que se registren beans idénticos en el mismo dominio. Si dicha prueba requiere acceso a MBeanServer
, considere marcarlo como sucio:
import javax.management.MBeanServer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class MyJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
assertThat(this.mBeanServer.getDomains()).contains("java.lang");
// ...
}
}
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.context.SpringBootTest
import org.springframework.test.annotation.DirtiesContext
import javax.management.MBeanServer
@SpringBootTest(properties = ["spring.jmx.enabled=true"])
@DirtiesContext
class MyJmxTests(@Autowired val mBeanServer: MBeanServer) {
@Test
fun exampleTest() {
assertThat(mBeanServer.domains).contains("java.lang")
// ...
}
}
Uso de métricas
Independientemente de la ruta de clases, los registros de contador distintos de los almacenados en la memoria no se configuran automáticamente cuando se usa la anotación @SpringBootTest
.
Si necesita exportar métricas a otro backend como parte de una prueba de integración, anótelo con @AutoConfigureMetrics
.
GO TO FULL VERSION