MockMvc ile yanıt gövdesinde Dize nasıl kontrol edilir


243

Basit entegrasyon testim var

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

Son satırda yanıt gövdesinde alınan dizeyi beklenen dizeyle karşılaştırmak istiyorum

Ve yanıt olarak:

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

İçerik (), gövde () ile bazı hileler denedim ama hiçbir şey işe yaramadı.


19
Tavsiyede olduğu gibi, 400 statü kodu gibi bir şey için iade edilmemelidir "Username already taken". Bu daha çok bir 409 Çatışması olmalı.
Sotirios Delimanolis

Thanx - bu testin amacı böyle şeyleri belirtmektir.
pbaranski

Yanıtlar:


356

İçeriği bir olarak almak için andReturn()döndürülen MvcResultnesneyi çağırabilir ve kullanabilirsiniz String.

Aşağıya bakınız:

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 Açıklayabilir misiniz? A @RestController, tüm işleyici yöntemlerinin örtülü olarak açıklandığı anlamına gelir @ResponseBody. Bu, Spring'in HttpMessageConverterişleyicinin dönüş değerini serileştirmek ve yanıta yazmak için a kullanacağı anlamına gelir . Vücudu çok fazla alabilirsin content().
Sotirios Delimanolis

5
@SotiriosDelimanolis doğru ... Şu anda açığa çıkan kontrol cihazımdan getContentAsString()gelen @RestControllerJSON'a bakıyorum.
Paul

Aradığım şeyi hata mesajında ​​buldum:result.getResponse().getErrorMessage()
whistling_marmot

andreturn () boş değer döndürüyor
Giriraj

@Giriraj , burada javadoc'ta belirtildiği gibi andReturna döndürür . MvcResult
Sotirios Delimanolis

105

@Sotirios Delimanolis cevap iş yapmak ancak bu mockMvc iddiasında dizeleri karşılaştırma arıyordum

İşte burada

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

Tabii ki iddiam başarısız oldu:

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

Çünkü:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Yani bu çalıştığının kanıtı!


17
Her ihtimale karşı birileri yaptım gibi, bilmek yardımsever olduğunu, dinamik kimlikleri ile mesaj olduğunu dize () yöntemi ayrıca bir hamcrest kabul containsString eşleyici:.andExpect(content().string(containsString("\"Username already taken");
Molholm

4
@ TimBüthe, bu yanlış. Eğer böyle bir probleminiz varsa bunu bir soru olarak göndermelisiniz çünkü bu kesinlikle beklenen davranış ya da kendi kodumda şahit olduğum davranış değil.
Paul

2
İçe aktarmanın olduğunu unutmayın org.hamcrest.Matchers.containsString().
membersound

Ayrıca org.hamcrest.Matchers.equalToIgnoringWhiteSpace()tüm boşluk karakterlerini yok saymak için eşleştirici kullandım . Belki birisi için yararlı bir ipucu olacaktır
Iwo Kucharski

66

Spring MockMvc artık JSON için doğrudan desteğe sahip. Yani sadece şunu söylüyorsun:

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

ve dize karşılaştırmasından farklı olarak, "xyz alanı eksik" veya "mesaj" Beklenen "tamam" var "nok" gibi bir şey söyleyecektir.

Bu yöntem, 4.1 Yayında tanıtıldı.


2
tam bir örnek verebilir misiniz? ContentRequestMatchersBu özelliği de desteklemenize gerek yok mu?
Zarathustra

49

Bu cevapları okurken, Spring sürüm 4.x ile ilgili çok şey görebiliyorum, 3.2.0 sürümünü çeşitli nedenlerle kullanıyorum. Yani json gibi şeyler doğrudan doğrudan desteklenemez content().

Kullanmanın MockMvcResultMatchers.jsonPathgerçekten kolay olduğunu ve bir tedavi yaptığını buldum . Bir gönderi yöntemini test eden bir örnek.

Bu çözümün avantajı, tam json dizesi karşılaştırmalarına dayanmamakla, yine de özniteliklerle eşleşmenizdir.

(Kullanılıyor 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));

İstek gövdesi, sadece isterseniz gerçek bir json sahte veri dosyasından kolayca yükleyebileceğiniz sadece bir json dizgisiydi, ancak sorudan saptığı için buraya dahil etmedim.

Döndürülen gerçek json şöyle görünebilirdi:

{
    "data":"some value"
}

".andExpect (MockMvcResultMatchers.jsonPath ( "için şeref. $ veri".) değeri (expectedData))"
user1697575

28

İlkbaharın eğitiminden alınmıştır

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 şuradan edinilebilir import static org.hamcrest.Matchers.*;

jsonPath şuradan edinilebilir import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

ve jsonPathreferans burada bulunabilir


1
Ben olsun error: incompatible types: RequestMatcher cannot be converted to ResultMatcher için.andExpect(content().contentType(contentType))
Ian Vaughan

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

23

Bahar güvenliği @WithMockUserve hamcrest'in containsStringeşleştiricisi basit ve zarif bir çözüm sunar:

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

Github hakkında daha fazla örnek


4

JSON yanıtının nasıl ayrıştırılacağına ve JSON biçiminde bir fasulye ile nasıl istek gönderileceğine dair bir örnek:

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

Burada gördüğünüz gibi Bookbir istek DTO ve UpdateBookResponseJSON ayrıştırılmış bir yanıt nesnesidir. Jakson ObjectMapperyapılandırmasını değiştirmek isteyebilirsiniz .


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

Bu size yanıtın bedenini vermelidir. Durumunuzda "kullanıcı adı zaten alınmış".


açıklama nerede? gerekli ya da yorumda bu tür bir cevap verebilirsiniz
user1140237

2

burada daha zarif bir yol

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

2

Yanıt verilerini dize olarak almak için 'getContentAsString' yöntemini kullanabilirsiniz.

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

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

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

Test uygulaması için bu bağlantıya başvurabilirsiniz .


1

Olası bir yaklaşım basitçe gsonbağımlılığı dahil etmektir :

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

ve doğrulamalarınızı yapmak için değeri ayrıştırın:

@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!");
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.