Cara memeriksa String di badan respons dengan mockMvc


243

Saya memiliki tes integrasi sederhana

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

Pada baris terakhir saya ingin membandingkan string yang diterima di tubuh respons dengan string yang diharapkan

Dan sebagai tanggapan saya mendapatkan:

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

Mencoba beberapa trik dengan konten (), tubuh () tetapi tidak ada yang berhasil.


19
Sama seperti saran, 400 kode status tidak boleh dikembalikan untuk sesuatu seperti "Username already taken". Itu seharusnya lebih dari 409 Konflik.
Sotirios Delimanolis

Terima kasih - tujuan dari tes ini adalah untuk menentukan hal-hal seperti itu.
pbaranski

Jawaban:


356

Anda dapat memanggil andReturn()dan menggunakan MvcResultobjek yang dikembalikan untuk mendapatkan konten sebagai String.

Lihat di bawah:

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 Bisakah Anda mengklarifikasi? A @RestControllermenunjukkan bahwa semua metode handler secara implisit dijelaskan dengan @ResponseBody. Ini berarti bahwa Spring akan menggunakan a HttpMessageConverteruntuk membuat cerita bersambung dari nilai pengembalian pawang dan menuliskannya pada respons. Anda bisa mendapatkan tubuh content().
Sotirios Delimanolis

5
@ SotiriosDelimanolis benar ... Saya melihat sekarang pada JSON yang dikembalikan oleh getContentAsString()yang datang dari @RestControllerpengontrol -anotot saya .
Paul

Saya menemukan apa yang saya cari dalam pesan kesalahan:result.getResponse().getErrorMessage()
whistling_marmot

andReturn () mengembalikan nilai nol
Giriraj

@Giriraj andReturnmengembalikan a MvcResult, sebagaimana ditentukan dalam javadoc di sini .
Sotirios Delimanolis

105

@Sotirios Delimanolis menjawab melakukan pekerjaan tetapi saya mencari untuk membandingkan string dalam pernyataan mockMvc

Jadi begini

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

Tentu saja pernyataan saya gagal:

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

karena:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Jadi ini adalah bukti bahwa itu berhasil!


17
Hanya dalam kasus seseorang memiliki pesan dengan ID yang dinamis, seperti yang saya lakukan, itu adalah membantu untuk mengetahui bahwa string () metode juga menerima Hamcrest containsString pencocok:.andExpect(content().string(containsString("\"Username already taken");
Molholm

4
@ TimBüthe, itu tidak benar. Jika Anda punya masalah seperti itu, Anda harus mempostingnya sebagai pertanyaan karena itu jelas bukan perilaku yang diharapkan juga bukan perilaku yang saya saksikan dalam kode saya sendiri.
Paul

2
Perhatikan bahwa impornya adalah org.hamcrest.Matchers.containsString().
membersound

Saya juga menggunakan org.hamcrest.Matchers.equalToIgnoringWhiteSpace()pencocokan untuk mengabaikan semua karakter spasi putih. Mungkin itu akan menjadi tip yang berguna bagi seseorang
Iwo Kucharski

66

Spring MockMvc sekarang memiliki dukungan langsung untuk JSON. Jadi, Anda hanya mengatakan:

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

dan tidak seperti perbandingan string, ia akan mengatakan sesuatu seperti "field xyz hilang" atau "pesan Diharapkan 'ok' got 'nok'.

Metode ini diperkenalkan di Spring 4.1.


2
dapatkah Anda memberikan contoh lengkap? Tidak perlu ContentRequestMatchersmendukung fitur ini juga?
Zarathustra

49

Membaca jawaban ini, saya bisa melihat banyak yang berkaitan dengan Spring versi 4.x, saya menggunakan versi 3.2.0 karena berbagai alasan. Jadi hal-hal seperti dukungan json langsung dari content()tidak mungkin.

Saya menemukan bahwa menggunakan MockMvcResultMatchers.jsonPathsangat mudah dan bekerja dengan baik. Berikut adalah contoh pengujian metode posting.

Bonus dengan solusi ini adalah Anda masih cocok dengan atribut, tidak mengandalkan perbandingan string json penuh.

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

Badan permintaan hanyalah string json, yang dapat Anda muat dengan mudah dari file data mock json asli jika Anda mau, tapi saya tidak memasukkannya di sini karena akan menyimpang dari pertanyaan.

Json yang sebenarnya kembali akan terlihat seperti ini:

{
    "data":"some value"
}

kudos untuk ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). value (expectedData))"
user1697575

28

Diambil dari tutorial musim semi

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 tersedia dari import static org.hamcrest.Matchers.*;

jsonPath tersedia dari import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

dan jsonPathreferensi dapat ditemukan di sini


1
Saya mendapatkan error: incompatible types: RequestMatcher cannot be converted to ResultMatcher untuk.andExpect(content().contentType(contentType))
Ian Vaughan

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

23

@WithMockUserPencocokan spring security dan hamcrest containsStringmembuat solusi sederhana dan elegan:

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

Lebih banyak contoh di github


4

Berikut adalah contoh cara mem-parsing respons JSON dan bahkan cara mengirim permintaan dengan kacang dalam bentuk 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());
  }

Seperti yang Anda lihat di sini Bookadalah permintaan DTO dan UpdateBookResponseobjek respons diuraikan dari JSON. Anda mungkin ingin mengubah ObjectMapperkonfigurasi Jakson .


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

Ini akan memberi Anda tubuh respons. "Nama pengguna sudah diambil" dalam kasus Anda.


mana penjelasannya? diperlukan atau Anda dapat memberikan komentar jenis jawaban ini
user1140237

2

di sini cara yang lebih elegan

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

2

Anda dapat menggunakan metode 'getContentAsString' untuk mendapatkan data respons sebagai string.

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

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

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

Anda dapat merujuk tautan ini untuk aplikasi uji.


1

Salah satu pendekatan yang mungkin adalah dengan memasukkan gsonketergantungan:

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

dan parsing nilai untuk membuat verifikasi Anda:

@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!");
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.