วิธีการตรวจสอบ String ในการตอบสนองเนื้อหาด้วย mockMvc


243

ฉันมีการทดสอบการรวมอย่างง่าย

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

ในบรรทัดสุดท้ายฉันต้องการเปรียบเทียบสตริงที่ได้รับในเนื้อความการตอบสนองกับสตริงที่คาดหวัง

และในการตอบสนองฉันได้รับ:

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

ลองใช้เทคนิคบางอย่างกับเนื้อหา (), เนื้อหา () แต่ไม่มีอะไรทำงาน


19
เช่นเดียวกับคำแนะนำ 400 "Username already taken"รหัสสถานะไม่ควรจะกลับไปหาสิ่งที่ต้องการ นั่นควรมากกว่า 409 ความขัดแย้ง
Sotirios Delimanolis

Thanx - เป้าหมายของการทดสอบนี้คือการระบุสิ่งต่าง ๆ
pbaranski

คำตอบ:


356

คุณสามารถโทรandReturn()และการใช้งานที่ส่งคืนวัตถุที่จะได้รับเนื้อหาที่เป็นMvcResultString

ดูด้านล่าง:

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คุณช่วยอธิบายได้ไหม? แสดงให้เห็นว่าวิธีการจัดการทั้งหมดจะข้อเขียนโดยปริยายด้วย@RestController @ResponseBodyซึ่งหมายความว่า Spring จะใช้ a HttpMessageConverterเพื่อซีเรียลไลซ์ค่าการส่งคืนของตัวจัดการและเขียนไปยังการตอบกลับ content()มากจะได้รับร่างกายด้วย
Sotirios Delimanolis

5
@SotiriosDelimanolis ถูกต้อง ... ฉันกำลังมองหาที่ JSON ที่ส่งคืนโดยgetContentAsString()ที่มาจาก@RestControllerตัวควบคุมที่ไม่ได้ระบุไว้ของฉัน
พอล

ฉันพบสิ่งที่ฉันกำลังมองหาในข้อความแสดงข้อผิดพลาด:result.getResponse().getErrorMessage()
whistling_marmot

andReturn () กำลังส่งคืนค่า Null
Giriraj

@Giriraj andReturnกลับMvcResultตามที่ระบุไว้ใน Javadoc ที่นี่
Sotirios Delimanolis

105

@Sotirios Delimanolis ตอบรับงาน แต่ฉันกำลังมองหาการเปรียบเทียบสตริงภายในการยืนยัน mockMvc นี้

ดังนั้นนี่คือ

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

แน่นอนการยืนยันของฉันล้มเหลว:

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

เพราะ:

  MockHttpServletResponse:
            Body = "Something gone wrong"

ดังนั้นนี่คือหลักฐานว่ามันใช้งานได้!


17
ในกรณีที่มีคนที่มีข้อความที่มี ID แบบไดนามิกเช่นฉันก็เป็นประโยชน์ที่จะรู้ว่าวิธีการสตริง () ยังยอมรับ hamcrest ที่มีสตริงmatcher:.andExpect(content().string(containsString("\"Username already taken");
molholm

4
@ TimBütheนั่นไม่ถูกต้อง หากคุณมีปัญหาดังกล่าวคุณควรโพสต์เป็นคำถามเพราะนั่นไม่ใช่พฤติกรรมที่คาดหวังหรือพฤติกรรมที่ฉันได้เห็นในรหัสของฉันเอง
พอล

2
org.hamcrest.Matchers.containsString()เพียงแค่ทราบว่าการนำเข้า
membersound

ฉันยังใช้org.hamcrest.Matchers.equalToIgnoringWhiteSpace()ตัวจับคู่เพื่อละเว้นอักขระช่องว่างทั้งหมด อาจเป็นเคล็ดลับที่มีประโยชน์สำหรับใครบางคน
Iwo Kucharski

66

Spring MockMvc ตอนนี้มีการสนับสนุน JSON โดยตรง ดังนั้นคุณเพียงแค่พูดว่า:

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

และต่างจากการเปรียบเทียบสตริงมันจะพูดบางอย่างเช่น "ฟิลด์ที่ขาดหายไป xyz" หรือ "ข้อความคาดว่า 'ตกลง' มี 'nok'

วิธีการนี้ได้รับการแนะนำในฤดูใบไม้ผลิ 4.1


2
คุณสามารถให้ตัวอย่างเต็มรูปแบบ? ไม่จำเป็นต้องContentRequestMatchersสนับสนุนคุณสมบัตินี้เช่นกัน?
Zarathustra

49

การอ่านคำตอบเหล่านี้ฉันสามารถเห็นมากมายเกี่ยวกับ Spring version 4.x ฉันใช้ version 3.2.0 ด้วยเหตุผลหลายประการ ดังนั้นสิ่งที่ต้องการการสนับสนุน json โดยตรงจากที่content()เป็นไปไม่ได้

ฉันพบว่าการใช้MockMvcResultMatchers.jsonPathนั้นง่ายและใช้งานได้จริง นี่คือตัวอย่างการทดสอบวิธีการโพสต์

โบนัสด้วยวิธีนี้คือคุณยังคงจับคู่กับคุณสมบัติต่างๆโดยไม่ต้องพึ่งพาการเปรียบเทียบสตริง json แบบเต็ม

(ใช้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));

เนื้อหาคำร้องขอเป็นเพียงสตริง json ซึ่งคุณสามารถโหลดจากไฟล์ข้อมูลจำลอง json จริงได้หากคุณต้องการ แต่ฉันไม่ได้รวมไว้ที่นี่เพราะมันจะเบี่ยงเบนไปจากคำถาม

json จริงที่ส่งคืนจะมีลักษณะเช่นนี้:

{
    "data":"some value"
}

ความรุ่งโรจน์สำหรับ ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). value (คาดว่าข้อมูล))"
1697575

28

นำมาจากบทช่วยสอนของฤดูใบไม้ผลิ

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 หาได้จาก import static org.hamcrest.Matchers.*;

jsonPath หาได้จาก import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

และjsonPathการอ้างอิงสามารถพบได้ที่นี่


1
ฉันจะได้รับerror: incompatible types: RequestMatcher cannot be converted to ResultMatcher สำหรับ.andExpect(content().contentType(contentType))
เอียนวอห์น

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

23

ระบบจับคู่@WithMockUserและความปลอดภัยสปริงของHamcrest containsStringทำให้เป็นทางออกที่ง่ายและสง่างาม:

@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


4

นี่คือตัวอย่างวิธีแยกวิเคราะห์คำตอบของ JSON และวิธีส่งคำขอด้วย bean ในรูปแบบ 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());
  }

ดังที่คุณเห็นนี่Bookคือคำร้องขอ DTO และUpdateBookResponseอ็อบเจ็กต์การตอบกลับถูกวิเคราะห์คำจาก JSON คุณอาจต้องการเปลี่ยนการObjectMapperกำหนดค่าของ Jakson


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

สิ่งนี้จะให้ร่างกายของการตอบสนอง "ชื่อผู้ใช้ไปแล้ว" ในกรณีของคุณ


คำอธิบายนั้นมาจากไหน? มันเป็นสิ่งจำเป็นหรือคุณสามารถให้มันในความคิดเห็นคำตอบประเภทนี้
1140237


2

คุณสามารถใช้วิธีการ 'getContentAsString' เพื่อรับข้อมูลการตอบสนองเป็นสตริง

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

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

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

คุณสามารถอ้างอิงลิงค์นี้สำหรับแอปพลิเคชันทดสอบ


1

วิธีหนึ่งที่เป็นไปได้คือการรวมการgsonพึ่งพา:

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

และแยกค่าเพื่อทำการตรวจสอบของคุณ:

@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!");
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.