web-dev-qa-db-fra.com

Comment vérifier String dans le corps de la réponse avec mockMvc

J'ai un test d'intégration simple

    @Test
public void shouldReturnErrorMessageToAdminWhenCreatingUserWithUsedUserName() throws Exception {
    mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
            .content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
            .andDo(print())
            .andExpect(status().isBadRequest())
            .andExpect(?);
}

En dernière ligne, je souhaite comparer la chaîne reçue dans le corps de la réponse à la chaîne attendue

Et en réponse je reçois:

MockHttpServletResponse:
          Status = 400
   Error message = null
         Headers = {Content-Type=[application/json]}
    Content type = application/json
            Body = "Username already taken"
   Forwarded URL = null
  Redirected URL = null

J'ai essayé quelques astuces avec content (), body () mais rien n'a fonctionné.

194
pbaranski

La réponse de @Sotirios Delimanolis fait le travail mais je cherchais des chaînes de comparaison dans cette affirmation fictive

Alors voila

.andExpect(content().string("\"Username already taken - please try with different username\""));

Bien sûr, mon assertion échoue:

Java.lang.AssertionError: Response content expected:
<"Username already taken - please try with different username"> but was:<"Something gone wrong">

parce que:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Donc, c'est la preuve que ça marche!

87
pbaranski

Vous pouvez appeler andReturn() et utiliser l'objet MvcResult renvoyé pour obtenir le contenu sous forme de String.

Voir ci-dessous:

MvcResult result = mockMvc.perform(post("/api/users").header("Authorization", base64ForTestUser).contentType(MediaType.APPLICATION_JSON)
            .content("{\"userName\":\"testUserDetails\",\"firstName\":\"xxx\",\"lastName\":\"xxx\",\"password\":\"xxx\"}"))
            .andDo(MockMvcResultHandlers.print())
            .andExpect(status().isBadRequest())
            .andReturn();

String content = result.getResponse().getContentAsString();
// do what you will
297

Spring MockMvc prend désormais en charge directement JSON. Alors vous dites juste:

.andExpect(content().json("{'message':'ok'}"));

et contrairement à la comparaison de chaînes, il va dire quelque chose comme "champ manquant xyz" ou "message attendu" ok "obtenu" nok ".

Cette méthode a été introduite au printemps 4.1.

55
vertti

En lisant ces réponses, je vois beaucoup de choses sur Spring version 4.x. J'utilise la version 3.2.0 pour diverses raisons. Donc, des choses comme le support json directement à partir de la content() ne sont pas possibles.

J'ai trouvé que l'utilisation de MockMvcResultMatchers.jsonPath est très simple et fonctionne très bien. Voici un exemple de test d'une méthode de publication.

L'avantage de cette solution est que vous faites toujours correspondre les attributs, sans vous fier aux comparaisons complètes de chaînes JSON.

(Utilisation de org.springframework.test.web.servlet.result.MockMvcResultMatchers)

String expectedData = "some value";
mockMvc.perform(post("/endPoint")
                .contentType(MediaType.APPLICATION_JSON)
                .content(mockRequestBodyAsString.getBytes()))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data").value(expectedData));

Le corps de la demande était juste une chaîne json, que vous pouvez facilement charger à partir d’un fichier de données simulé si vous le vouliez, mais je n’ai pas inclus cela ici car cela aurait dévié de la question.

Le JSON retourné aurait ressemblé à ceci:

{
    "data":"some value"
}
42
Jeremy

Le @WithMockUser de Spring Security et le matcher containsString de hamcrest constituent une solution simple et élégante:

@Test
@WithMockUser(roles = "USER")
public void loginWithRoleUserThenExpectUserSpecificContent() throws Exception {
    mockMvc.perform(get("/index"))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("This content is only shown to users.")));
}

Plus d'exemples sur github

19
Michael W

Tiré du printemps tutoriel

    mockMvc.perform(get("/" + userName + "/bookmarks/"
            + this.bookmarkList.get(0).getId()))
            .andExpect(status().isOk())
            .andExpect(content().contentType(contentType))
            .andExpect(jsonPath("$.id", is(this.bookmarkList.get(0).getId().intValue())))
            .andExpect(jsonPath("$.uri", is("http://bookmark.com/1/" + userName)))
            .andExpect(jsonPath("$.description", is("A description")));

is est disponible à partir de import static org.hamcrest.Matchers.*;

jsonPath est disponible à partir de import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

et jsonPath référence peut être trouvé ici

19
user2829759

voici une manière plus élégante

mockMvc.perform(post("/retrieve?page=1&countReg=999999")
            .header("Authorization", "Bearer " + validToken))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("regCount")));
1
user6266697
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()

Cela devrait vous donner le corps de la réponse. "Nom d'utilisateur déjà pris" dans votre cas.

1
justAnotherGuy

Voici un exemple comment analyser une réponse JSON et même comment envoyer une demande avec un bean au format JSON:

  @Autowired
  protected MockMvc mvc;

  private static final ObjectMapper MAPPER = new ObjectMapper()
    .configure(WRITE_DATES_AS_TIMESTAMPS, false)
    .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
    .registerModule(new JavaTimeModule());

  public static String requestBody(Object request) {
    try {
      return MAPPER.writeValueAsString(request);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

  public static <T> T parseResponse(MvcResult result, Class<T> responseClass) {
    try {
      String contentAsString = result.getResponse().getContentAsString();
      return MAPPER.readValue(contentAsString, responseClass);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  @Test
  public void testUpdate() {
    Book book = new Book();
    book.setTitle("1984");
    book.setAuthor("Orwell");
    MvcResult requestResult = mvc.perform(post("http://example.com/book/")
      .contentType(MediaType.APPLICATION_JSON)
      .content(requestBody(book)))
      .andExpect(status().isOk())
      .andReturn();
    UpdateBookResponse updateBookResponse = parseResponse(requestResult, UpdateBookResponse.class);
    assertEquals("1984", updateBookResponse.getTitle());
    assertEquals("Orwell", updateBookResponse.getAuthor());
  }

Comme vous pouvez le voir ici, la Book est une requête DTO et la UpdateBookResponse un objet de réponse analysé à partir de JSON. Vous voudrez peut-être changer la configuration de Jakson ObjectMapper.

0
stokito