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.

Las propiedades externas, el registro y otras características de Spring Boot se instalan en el contexto predeterminado solo si usa 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.

Si está utilizando JUnit 4, no olvide agregar tambié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 web ApplicationContext 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 un ApplicationContext 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: carga WebServerApplicationContext y proporciona el entorno web real. Los servidores integrados inician y escuchan en un puerto aleatorio.

  • DEFINED_PORT: carga el WebServerApplicationContext y proporciona el entorno web real. Los servidores integrados se inician y escuchan en el puerto especificado (desde el archivo application.properties) o en el puerto predeterminado 8080.

  • NONE: carga ApplicationContext usando SpringApplication, pero no proporciona ningún entorno web (simulado o no).

Si su prueba está marcada con la anotación @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.
La anotación @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:

Java
 import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests {
    // ...
} 
Kotlin
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(properties = ["spring.main.web-application-type=reactive"])
class MyWebFluxTests {
    // ...
}

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.

El marco de pruebas de Spring almacena en caché los contextos de la aplicación entre pruebas. Por lo tanto, siempre que las pruebas utilicen la misma configuración (independientemente de cómo se descubrió), el proceso de carga del contexto, que potencialmente consume muchos recursos, se produce solo una vez.

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:

Java

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
    @Test
    void exampleTest() {
        // ...
    }
}
        
Kotlin

import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import
@SpringBootTest
@Import(MyTestsConfiguration::class)
class MyTests {
    @Test
    fun exampleTest() {
        // ...
    }
}
If La anotación @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.

Java

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");
    }
}
Kotlin

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:

Java

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");
    }
}
Kotlin

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")
    }
}
Si necesita centrarse solo en la capa web y no ejecutar el 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:

Java

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");
    }
}
Kotlin

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:

Java

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");
    }
}
Kotlin

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:

Java
 
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");
    }
}
Kotlin

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:

Java

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

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.