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.

pbaranski
sumber
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 
Sotirios Delimanolis
sumber
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!

pbaranski
sumber
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.

vertti
sumber
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"
}
Jeremy
sumber
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

pengguna2829759
sumber
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

Michael W
sumber
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 .

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

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

justAnotherGuy
sumber
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")));
Ricardo Ribeiro
sumber
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.

Hari Krishna
sumber
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!");
    }
}
Koray Tugay
sumber