Amélioration des tests Spring Mock-MVC

Spring Mock - MVC  fournit d'excellentes méthodes de test pour les API REST Spring Boot. Mock-MVC nous permet de tester la gestion des requêtes Spring-MVC sans démarrer un vrai serveur.

J'ai utilisé des tests Mock-MVC sur divers projets et d'après mon expérience, ils sont souvent assez verbeux. Ce n'est pas nécessairement une mauvaise chose. Cependant, cela nécessite souvent de copier / coller des fragments de code similaires dans des classes de test. Dans cet article, nous examinerons plusieurs façons d'améliorer les tests Spring Mock-MVC.

Décidez quoi tester avec Mock-MVC

La première question que nous devons nous poser est ce que nous voulons tester avec Mock-MVC. Voici quelques exemples de cas de test:

  • Tester uniquement la couche Web et émuler toutes les dépendances de contrôleur.

  • Test du niveau Web avec la logique de domaine et simulation de dépendances tierces telles que des bases de données ou des files d'attente de messages.

  • Tester le chemin complet de la couche Web à la base de données en remplaçant les dépendances tierces par des alternatives intégrées si possible (par exemple  H2  ou  Embedded-Kafka  )

Tous ces scénarios ont leurs avantages et leurs inconvénients. Cependant, je pense qu'il y a deux règles simples que nous devons suivre:

  • Testez autant que possible dans les tests JUnit standard (pas de Spring). Cela améliore considérablement les performances des tests et facilite l'écriture des 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  .

Vous pouvez trouver l'exemple de code sur  GitHub .




All Articles