Diferencia entre usar MockMvc con SpringBootTest y usar WebMvcTest

96

Soy nuevo en Spring Boot y estoy tratando de entender cómo funcionan las pruebas en SpringBoot. Estoy un poco confundido acerca de cuál es la diferencia entre los siguientes dos fragmentos de código:

Fragmento de código 1:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerApplicationTest {
    @Autowired    
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Esta prueba usa la @WebMvcTestanotación que creo que es para pruebas de segmentos de características y solo prueba la capa MVC de una aplicación web.

Fragmento de código 2:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Esta prueba usa la @SpringBootTestanotación y un MockMvc. Entonces, ¿en qué se diferencia esto del fragmento de código 1? ¿Qué hace esto de manera diferente?

Editar: Agregar fragmento de código 3 (Encontré esto como un ejemplo de prueba de integración en la documentación de Spring)

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HelloControllerIT {
    
    @LocalServerPort private int port;
    private URL base;
    
    @Autowired private TestRestTemplate template;
    
    @Before public void setUp() throws Exception {
        this.base = new URL("http://localhost:" + port + "/");
    }
    
    @Test public void getHello() throws Exception {
        ResponseEntity < String > response = template.getForEntity(base.toString(), String.class);
        assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
    }
}
Revansha
fuente

Respuestas:

88

@SpringBootTestes la anotación de prueba general. Si está buscando algo que haga lo mismo antes de 1.4, ese es el que debe usar. No usa rebanar lo que significa que iniciará el contexto completo de la aplicación y no personalizará el escaneo de componentes en absoluto.

@WebMvcTestsolo escaneará el controlador que ha definido y la infraestructura MVC. Eso es. Entonces, si su controlador tiene alguna dependencia de otros beans de su capa de servicio, la prueba no comenzará hasta que cargue esa configuración usted mismo o proporcione una simulación. Esto es mucho más rápido ya que solo cargamos una pequeña parte de su aplicación. Esta anotación utiliza la división.

Leer el documento probablemente también debería ayudarte.

Stephane Nicoll
fuente
¡¡Muchas gracias por responder !!. Entonces, si lo entiendo correctamente, lo que eso significa es que ambos fragmentos de código prueban solo la parte MVC de la aplicación. Pero el fragmento de código 1 carga el contexto completo de la aplicación, mientras que el fragmento de código 2 solo escanea el controlador. ¿Es esto correcto? ¿Se puede considerar el fragmento de código 1 como una prueba unitaria para probar el controlador?
Revansha
1
No, no es correcto. SpringBootTestestá cargando su aplicación completa (hasta cierto punto, de forma predeterminada no iniciará el contenedor incrustado si hay uno disponible, para eso webEnvironmentestá). No diría que @SpringBootTestes una prueba unitaria del controlador, sino más una prueba de integración, en realidad. WebMvcTestes realmente una prueba unitaria de su controlador en el sentido de que si tiene dependencia, tendrá que proporcionarlos usted mismo (ya sea una configuración o una simulación de algún tipo).
Stephane Nicoll
Gracias de nuevo por responder. Edité la pregunta y agregué el fragmento de código 3. Mencionaste que la anotación @SpringBootTest se usa más para las pruebas de integración. Creo que el fragmento 3 lo demuestra. Entonces, si las pruebas de integración se realizan como en el Snippet 3, ¿qué hace el Snippet 2? El fragmento 2 usa la anotación SpringBootTest y un entorno simulado (valor predeterminado del atributo wenEnvironment). Además, el fragmento 3 inicia el servidor integrado y realiza realmente llamadas HTTP, mientras que el fragmento 2 no lo hace. Entonces, considerando esto, ¿el fragmento 2 no puede considerarse una prueba unitaria?
Revansha
4
No estoy seguro de que vayamos a solucionar esto aquí. ¿Quizás gitter? Lo que parece perder constantemente es que el contexto de la aplicación que crean SpringBootTesty WebMvcTestcrea son muy diferentes. El primero carga TODA su aplicación y habilita TODAS las configuraciones automáticas, mientras que el segundo solo habilita Spring Mvc y no escanea nada más que HelloController. Después de todo, todo depende de lo que quieras decir con prueba unitaria. Pero esa es la diferencia.
Stephane Nicoll
Gracias por su respuesta. Eso es muy útil para mí. Ahora entiendo por qué mi prueba se puede ejecutar con SpringBootTest pero con excepción cuando WebMvcTest. Muchas gracias de nuevo.
Alpes 1992
69

@SpringBootTest anotación le dice a Spring Boot que busque una clase de configuración principal (una con @SpringBootApplication, por ejemplo), y úsela para iniciar un contexto de aplicación Spring. SpringBootTest carga la aplicación completa e inyecta todos los beans, lo que puede ser lento.

@WebMvcTest : para probar la capa del controlador y debe proporcionar las dependencias restantes requeridas mediante Mock Objects.

Algunas anotaciones más a continuación para su referencia.

Prueba de segmentos de la aplicación A veces, le gustaría probar un simple "segmento" de la aplicación en lugar de configurar automáticamente toda la aplicación. Spring Boot 1.4 presenta 4 nuevas anotaciones de prueba:

@WebMvcTest - for testing the controller layer
@JsonTest - for testing the JSON marshalling and unmarshalling
@DataJpaTest - for testing the repository layer
@RestClientTests - for testing REST clients

Consulte para obtener más información: https://spring.io/guides/gs/testing-web/

RoshanKumar Mutha
fuente
Aquí hay un enlace a Sping Boot Reference - Prueba de anotaciones de configuración automática . Hay más que los cuatro que @ roshankumar-mutha ha enumerado aquí. El enlace a la guía de introducción no cubre estas secciones en profundidad.
George Pantazes
15

Las pruebas MVC están destinadas a cubrir solo la parte del controlador de su aplicación. Las solicitudes y respuestas HTTP se simulan para que no se creen conexiones reales. Por otro lado, cuando lo utiliza @SpringBootTest, toda la configuración para el contexto de la aplicación web se carga y las conexiones pasan por el servidor web real. En ese caso, no usa el MockMvcbean sino un estándar RestTemplate(o la nueva alternativa TestRestTemplate).

Entonces, ¿cuándo deberíamos elegir uno u otro? @WebMvcTestestá destinado a probar unitariamente el controlador desde el lado del servidor. @SpringBootTest, por otro lado, debe usarse para pruebas de integración, cuando desee interactuar con la aplicación desde el lado del cliente.

Eso no significa que no puedas usar simulacros con @SpringBootTest ; si está escribiendo una prueba de integración, aún podría ser necesario. En cualquier caso, es mejor no usarlo solo para una simple prueba unitaria del controlador.

fuente - Aprendizaje de microservicios con Spring Boot

Gautam Tadigoppula
fuente
1
No entiendo por qué se vota a favor esta respuesta. Cuando lo usa @SpringBootTest, un servidor web real no se inicia a menos que también tenga webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT(o a DEFINED_PORT) y las conexiones no pasen por el servidor web real. El valor predeterminado @SpringBootTestes WebEnvironment.MOCK.
Koray Tugay