Spring Mock- MVC bietet hervorragende Testmethoden für Spring Boot REST-APIs. Mit Mock-MVC können wir die Bearbeitung von Spring-MVC-Anforderungen testen, ohne einen echten Server zu starten.
Ich habe Mock-MVC-Tests für verschiedene Projekte verwendet und meiner Erfahrung nach sind sie oft sehr ausführlich. Das ist nicht unbedingt eine schlechte Sache. Dies erfordert jedoch häufig das Kopieren / Einfügen ähnlicher Codefragmente in Testklassen. In diesem Beitrag werden verschiedene Möglichkeiten zur Verbesserung der Spring Mock-MVC-Tests vorgestellt.
Entscheiden Sie, was mit Mock-MVC getestet werden soll
Die erste Frage, die wir stellen müssen, ist, was wir mit Mock-MVC testen möchten. Hier einige Beispiele für Testfälle:
Testen Sie nur die Webebene und emulieren Sie alle Controller-Abhängigkeiten.
Testen der Webschicht mit Domänenlogik und Simulieren von Abhängigkeiten von Drittanbietern wie Datenbanken oder Nachrichtenwarteschlangen.
Testen des vollständigen Pfads von der Webschicht zur Datenbank durch Ersetzen von Abhängigkeiten von Drittanbietern durch eingebettete Alternativen, wenn möglich (z. B. H2 oder Embedded-Kafka )
Alle diese Szenarien haben ihre Vor- und Nachteile. Ich denke jedoch, dass wir zwei einfache Regeln befolgen müssen:
Testen Sie so viel wie möglich in Standard-JUnit-Tests (keine Feder). Dies verbessert die Testleistung erheblich und erleichtert das Schreiben von Tests.
(-), 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 .
Sie finden die Codebeispiele auf GitHub .