Spring Mock- MVC fornece métodos de teste excelentes para APIs REST Spring Boot. O Mock-MVC nos permite testar o processamento de solicitações Spring-MVC sem iniciar um servidor real.
Eu usei testes Mock-MVC em uma variedade de projetos e, na minha experiência, eles costumam ser bastante prolixos. Isso não é necessariamente uma coisa ruim. No entanto, isso geralmente requer a cópia / colagem de fragmentos de código semelhantes em classes de teste. Nesta postagem, veremos várias maneiras de melhorar os testes do Spring Mock-MVC.
Decida o que testar com o Mock-MVC
A primeira pergunta que precisamos fazer é o que queremos testar com o Mock-MVC. Aqui estão alguns exemplos de casos de teste:
Testando apenas a camada da web e emulando todas as dependências do controlador.
Testar a camada da web com lógica de domínio e simular dependências de terceiros, como bancos de dados ou filas de mensagens.
Testar o caminho completo da camada da web para o banco de dados, substituindo dependências de terceiros por alternativas incorporadas, se possível (por exemplo, H2 ou Kafka incorporado )
Todos esses cenários têm seus prós e contras. No entanto, acho que existem duas regras simples que devemos seguir:
Teste o máximo possível em testes JUnit padrão (sem Spring). Isso melhora muito o desempenho do teste e torna mais fácil escrevê-los.
(-), 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 .
Você pode encontrar os exemplos de código no GitHub .