Mejora de las pruebas Spring Mock-MVC

Spring Mock- MVC  proporciona excelentes métodos de prueba para las API REST de Spring Boot. Mock-MVC nos permite probar el manejo de solicitudes Spring-MVC sin iniciar un servidor real.

He usado pruebas Mock-MVC en varios proyectos y, en mi experiencia, a menudo son bastante detalladas. Esto no es necesariamente algo malo. Sin embargo, esto a menudo requiere copiar / pegar fragmentos de código similares en clases de prueba. En esta publicación, veremos varias formas de mejorar las pruebas Spring Mock-MVC.

Decida qué probar con Mock-MVC

La primera pregunta que debemos hacernos es qué queremos probar con Mock-MVC. A continuación, se muestran algunos ejemplos de casos de prueba:

  • Probando solo la capa web y emulando todas las dependencias del controlador.

  • Probar el nivel web con lógica de dominio y simular dependencias de terceros, como bases de datos o colas de mensajes.

  • Probar la ruta completa desde la capa web a la base de datos reemplazando dependencias de terceros con alternativas integradas si es posible (por ejemplo,  H2  o  embedded-Kafka  )

Todos estos escenarios tienen sus pros y sus contras. Sin embargo, creo que hay dos reglas simples que debemos seguir:

  • Pruebe tanto como sea posible en las pruebas estándar de JUnit (sin Spring). Esto mejora enormemente el rendimiento de las pruebas y facilita la redacción de pruebas.

  • (-), Spring, , . . Spring , .

JUnit . , , Mock-MVC, , , .

Spring Spring .

, @MockMvcTest:

@SpringBootTest
@TestPropertySource(locations = "classpath:test.properties")
@AutoConfigureMockMvc(secure = false)
@Retention(RetentionPolicy.RUNTIME)
public @interface MockMvcTest {}

:

@MockMvcTest
public class MyTest {
    ...
}

, .  Spring .

Mock-MVC

Mock-MVC , :

mockMvc.perform(put("/products/42")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .content("{\"name\": \"Cool Gadget\", \"description\": \"Looks cool\"}")
        .header("Authorization", getBasicAuthHeader("John", "secr3t")))
        .andExpect(status().isOk());

PUT JSON /products/42.

, - JSON Java. , , , , Java, .

, JSON.  , .  Java Text  JDK 13/14 .  - , .

JSON . :

mvc.perform(put("/products/42")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .content("""
            {
                "name": "Cool Gadget",
                "description": "Looks cool"
            }
            """)
        .header("Authorization", getBasicAuthHeader("John", "secr3t")))
        .andExpect(status().isOk()); 

.

, -, , JSON , JSON.

:

Product product = new Product("Cool Gadget", "Looks cool");
mvc.perform(put("/products/42")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .content(objectToJson(product))
        .header("Authorization", getBasicAuthHeader("John", "secr3t")))
        .andExpect(status().isOk());

product JSON    objectToJson(..).  .  , .

, .  JSON REST-API, , PUT.  :

public static MockHttpServletRequestBuilder putJson(String uri, Object body) {
    try {
        String json = new ObjectMapper().writeValueAsString(body);
        return put(uri)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
                .content(json);
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

  body  JSON Jackson ObjectMapper .  PUT   Accept  Content-Type .

:

Product product = new Product("Cool Gadget", "Looks cool");
mvc.perform(putJson("/products/42", product)
        .header("Authorization", getBasicAuthHeader("John", "secr3t")))
        .andExpect(status().isOk())

, .   putJson(..)  MockHttpServletRequestBuilder.  , , (,    ).

- , Spring Mock-MVC.    putJson(..).  PUT , , -.

RequestPostProcessor  .  , RequestPostProcessor  .  .

:

public static RequestPostProcessor authentication() {
    return request -> {
        request.addHeader("Authorization", getBasicAuthHeader("John", "secr3t"));
        return request;
    };
} 

 authentication()  RequestPostProcessor,  .   RequestPostProcessor   with(..):

Product product = new Product("Cool Gadget", "Looks cool");
mvc.perform(putJson("/products/42", product).with(authentication()))
        .andExpect(status().isOk())

.  , , .  , putJson(url, data).with(authentication())  .

, .

:

mvc.perform(get("/products/42"))
        .andExpect(status().isOk())
        .andExpect(header().string("Cache-Control", "no-cache"))
        .andExpect(jsonPath("$.name").value("Cool Gadget"))
        .andExpect(jsonPath("$.description").value("Looks cool"));

HTTP, ,   Cache-Control   no-cache,  JSON-Path .

 Cache-Control  , ,  ,  .  :

public ResultMatcher noCacheHeader() {
    return header().string("Cache-Control", "no-cache");
}

,  noCacheHeader()  andExpect(..):

mvc.perform(get("/products/42"))
        .andExpect(status().isOk())
        .andExpect(noCacheHeader())
        .andExpect(jsonPath("$.name").value("Cool Gadget"))
        .andExpect(jsonPath("$.description").value("Looks cool"));

.

,   product(..),  JSON   Product:

public static ResultMatcher product(String prefix, Product product) {
    return ResultMatcher.matchAll(
            jsonPath(prefix + ".name").value(product.getName()),
            jsonPath(prefix + ".description").value(product.getDescription())
    );
}

:

Product product = new Product("Cool Gadget", "Looks cool");
mvc.perform(get("/products/42"))
        .andExpect(status().isOk())
        .andExpect(noCacheHeader())
        .andExpect(product("$", product));

,   prefix  . , , JSON .

, .    prefix  . :

Product product0 = ..
Product product1 = ..
mvc.perform(get("/products"))
        .andExpect(status().isOk())
        .andExpect(product("$[0]", product0))
        .andExpect(product("$[1]", product1));

   ResultMatcher  .  .

Spring Mock-MVC.  Mock-MVC, , .  ( Spring Mock-MVC).

Spring Mock-MVC.   RequestPostProcessor  .  ResultMatcher  .

Puede encontrar los ejemplos de código en  GitHub .




All Articles