Come controllare String nel corpo della risposta con mockMvc


243

Ho un semplice test di integrazione

@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(?);
}

Nell'ultima riga voglio confrontare la stringa ricevuta nel corpo della risposta con la stringa prevista

E in risposta ottengo:

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

Ho provato alcuni trucchi con content (), body () ma niente ha funzionato.


19
Proprio come consiglio, il codice di stato 400 non dovrebbe essere restituito per qualcosa del genere "Username already taken". Questo dovrebbe essere più di un conflitto 409.
Sotirios Delimanolis,

Grazie - l'obiettivo di questo test è quello di specificare tali cose.
pbaranski,

Risposte:


356

È possibile chiamare andReturn()e utilizzare l' MvcResultoggetto restituito per ottenere il contenuto come a String.

Vedi sotto:

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 

7
@ TimBüthe Puoi chiarire? A @RestControllerindica che tutti i metodi del gestore sono annotati in modo implicito @ResponseBody. Ciò significa che Spring utilizzerà a HttpMessageConverterper serializzare il valore di ritorno del gestore e scriverlo nella risposta. Puoi davvero ottenere il corpo con content().
Sotirios Delimanolis,

5
@SotiriosDelimanolis ha ragione ... Sto guardando ora il JSON restituito da getContentAsString()quello che proviene dal mio @RestControllercontroller non annotato.
Paul,

Ho trovato quello che cercavo nel messaggio di errore:result.getResponse().getErrorMessage()
whistling_marmot

andReturn () restituisce un valore null
Giriraj

@Giriraj andReturnrestituisce a MvcResult, come specificato nel javadoc qui .
Sotirios Delimanolis,

105

La risposta di @Sotirios Delimanolis fa il lavoro, tuttavia stavo cercando di confrontare le stringhe all'interno di questa affermazione mockMvc

Quindi eccolo qui

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

Naturalmente la mia affermazione fallisce:

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

perché:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Quindi questa è la prova che funziona!


17
Nel caso in cui qualcuno abbia messaggi con ID dinamici, come ho fatto io, è utile sapere che il metodo string () accetta anche un Hamcrest contiene Matcher di stringhe :.andExpect(content().string(containsString("\"Username already taken");
molholm

4
@ TimBüthe, non è corretto. Se hai un problema del genere, dovresti pubblicarlo come una domanda perché quello non è sicuramente il comportamento previsto né è il comportamento a cui ho assistito nel mio codice.
Paul,

2
Basta notare che l'importazione è org.hamcrest.Matchers.containsString().
membri del

Ho anche usato org.hamcrest.Matchers.equalToIgnoringWhiteSpace()Matcher per ignorare tutti i caratteri degli spazi bianchi. Forse sarà un suggerimento utile per qualcuno
Iwo Kucharski

66

Spring MockMvc ora supporta direttamente JSON. Quindi dici solo:

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

e diversamente dal confronto di stringhe, dirà qualcosa come "campo mancante xyz" o "messaggio Previsto 'ok' got 'nok'.

Questo metodo è stato introdotto nella primavera 4.1.


2
potresti fornire un esempio completo? Non è necessario ContentRequestMatcherssupportare anche questa funzione?
Zarathustra,

49

Leggendo queste risposte, posso vedere molte cose relative alla versione 4.x di primavera, sto usando la versione 3.2.0 per vari motivi. Quindi cose come il supporto json direttamente dal content()non sono possibili.

Ho scoperto che l'utilizzo MockMvcResultMatchers.jsonPathè davvero semplice e funziona a meraviglia. Ecco un esempio di test di un metodo post.

Il vantaggio di questa soluzione è che stai ancora confrontando gli attributi, senza fare affidamento sul confronto completo delle stringhe json.

(Utilizzando 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));

Il corpo della richiesta era solo una stringa json, che puoi facilmente caricare da un vero file di dati json se lo volevi, ma non l'ho incluso qui perché sarebbe deviato dalla domanda.

L'attuale json restituito sarebbe stato così:

{
    "data":"some value"
}

complimenti per ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). value (expectedData))"
user1697575

28

Tratto dal tutorial di primavera

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 è disponibile da import static org.hamcrest.Matchers.*;

jsonPath è disponibile da import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

e il jsonPathriferimento può essere trovato qui


1
Ricevo error: incompatible types: RequestMatcher cannot be converted to ResultMatcher per.andExpect(content().contentType(contentType))
Ian Vaughan,

@IanVaughan MockMvcResultMatchers.content (). ContentType (contentType)
Rajkumar,

23

La sicurezza di primavera @WithMockUsere quella di Hamcrest containsStringrappresentano una soluzione semplice ed elegante:

@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.")));
}

Altri esempi su github


4

Ecco un esempio su come analizzare la risposta JSON e persino su come inviare una richiesta con un bean in formato JSON:

  @Autowired
  protected MockMvc mvc;

  private static final ObjectMapper MAPPER = new ObjectMapper()
    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    .configure(DeserializationFeature.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());
  }

Come puoi vedere qui Bookè un DTO di richiesta e UpdateBookResponseun oggetto di risposta analizzato da JSON. Potresti voler cambiare la ObjectMapperconfigurazione di Jakson .


2
String body = mockMvc.perform(bla... bla).andReturn().getResolvedException().getMessage()

Questo dovrebbe darti il ​​corpo della risposta. "Nome utente già utilizzato" nel tuo caso.


dov'è la spiegazione? è necessario o puoi commentarlo con questo tipo di risposta
user1140237,

2

qui un modo più elegante

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

2

È possibile utilizzare il metodo 'getContentAsString' per ottenere i dati di risposta come stringa.

    String payload = "....";
    String apiToTest = "....";

    MvcResult mvcResult = mockMvc.
                perform(post(apiToTest).
                content(payload).
                contentType(MediaType.APPLICATION_JSON)).
                andReturn();

    String responseData = mvcResult.getResponse().getContentAsString();

È possibile fare riferimento a questo collegamento per l'applicazione di prova.


1

Un possibile approccio è semplicemente includere la gsondipendenza:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>

e analizzare il valore per effettuare le verifiche:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private HelloService helloService;

    @Before
    public void before() {
        Mockito.when(helloService.message()).thenReturn("hello world!");
    }

    @Test
    public void testMessage() throws Exception {
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/"))
                .andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE))
                .andReturn();

        String responseBody = mvcResult.getResponse().getContentAsString();
        HelloController.ResponseDto responseDto
                = new Gson().fromJson(responseBody, HelloController.ResponseDto.class);
        Assertions.assertThat(responseDto.message).isEqualTo("hello world!");
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.