Cách kiểm tra String trong phần thân phản hồi với mockMvc


243

Tôi có bài kiểm tra tích hợp đơn giản

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

Trong dòng cuối cùng tôi muốn so sánh chuỗi nhận được trong phần phản hồi với chuỗi dự kiến

Và đáp lại tôi nhận được:

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

Đã thử một số thủ thuật với nội dung (), body () nhưng không có gì hiệu quả.


19
Đúng như lời khuyên, 400 mã trạng thái không nên được trả lại cho một cái gì đó như "Username already taken". Đó phải là một cuộc xung đột 409.
Sotirios Delimanolis

Thanx - mục tiêu của bài kiểm tra này là chỉ định những điều như vậy.
pbaranski

Câu trả lời:


356

Bạn có thể gọi andReturn()và sử dụng MvcResultđối tượng trả về để lấy nội dung dưới dạng a String.

Xem bên dưới:

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 Bạn có thể làm rõ? A @RestControllerchỉ ra rằng tất cả các phương thức xử lý được chú thích ngầm với @ResponseBody. Điều này có nghĩa là Spring sẽ sử dụng a HttpMessageConverterđể tuần tự hóa giá trị trả về của trình xử lý và ghi nó vào phản hồi. Bạn rất có thể có được cơ thể với content().
Sotirios Delimanolis

5
@SotiriosDelimanolis là chính xác ... Hiện tại tôi đang tìm kiếm JSON được trả về getContentAsString()từ @RestControllerbộ điều khiển được bảo trợ của tôi .
Paul

Tôi đã tìm thấy những gì tôi đang tìm kiếm trong thông báo lỗi:result.getResponse().getErrorMessage()
huýt

andReturn () đang trả về giá trị null
Giriraj

@Giriraj andReturntrả về a MvcResult, như được chỉ định trong javadoc tại đây .
Sotirios Delimanolis

105

@Sotirios Delimanolis trả lời thực hiện công việc tuy nhiên tôi đang tìm cách so sánh các chuỗi trong xác nhận mockMvc này

Nó đây rồi

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

Tất nhiên khẳng định của tôi thất bại:

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

bởi vì:

  MockHttpServletResponse:
            Body = "Something gone wrong"

Vì vậy, đây là bằng chứng rằng nó hoạt động!


17
Chỉ trong trường hợp ai đó có tin nhắn với ID động, như tôi đã làm, thật hữu ích khi biết rằng phương thức chuỗi () cũng chấp nhận một hamcrest có chứa Trình so khớp:.andExpect(content().string(containsString("\"Username already taken");
molholm

4
@ TimBüthe, điều đó không chính xác. Nếu bạn gặp vấn đề như vậy, bạn nên đăng nó dưới dạng câu hỏi vì đó chắc chắn không phải là hành vi được mong đợi cũng không phải là hành vi tôi đã chứng kiến ​​trong mã của riêng mình.
Paul

2
Chỉ cần lưu ý rằng nhập khẩu là org.hamcrest.Matchers.containsString().
viên

Tôi cũng đã sử dụng org.hamcrest.Matchers.equalToIgnoringWhiteSpace()matcher để bỏ qua tất cả các ký tự khoảng trắng. Có lẽ nó sẽ là lời khuyên hữu ích cho ai đó
Iwo Kucharski

66

Spring MockMvc hiện có hỗ trợ trực tiếp cho JSON. Vì vậy, bạn chỉ cần nói:

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

và không giống như so sánh chuỗi, nó sẽ nói một cái gì đó như "thiếu trường xyz" hoặc "tin nhắn Dự kiến ​​'ok' got 'nok'.

Phương pháp này được giới thiệu vào mùa xuân 4.1.


2
bạn có thể cung cấp một ví dụ đầy đủ? Không cần ContentRequestMatchershỗ trợ tính năng này là tốt?
Zarathustra

49

Đọc những câu trả lời này, tôi có thể thấy rất nhiều liên quan đến phiên bản Spring 4.x, tôi đang sử dụng phiên bản 3.2.0 vì nhiều lý do. Vì vậy, những thứ như json hỗ trợ trực tiếp từ content()là không thể.

Tôi thấy rằng việc sử dụng MockMvcResultMatchers.jsonPathlà thực sự dễ dàng và làm việc một điều trị. Dưới đây là một ví dụ thử nghiệm một phương pháp bài.

Phần thưởng với giải pháp này là bạn vẫn phù hợp với các thuộc tính, không dựa vào so sánh chuỗi json đầy đủ.

(Sử dụng 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));

Phần thân yêu cầu chỉ là một chuỗi json, bạn có thể dễ dàng tải từ tệp dữ liệu giả json thực nếu bạn muốn, nhưng tôi không bao gồm nó ở đây vì nó sẽ bị lệch khỏi câu hỏi.

Json thực tế được trả lại sẽ trông như thế này:

{
    "data":"some value"
}

kudos cho ".andExpect (MockMvcResultMatchers.jsonPath (" $. data "). value (kỳ
vọngData

28

Lấy từ hướng dẫn của mùa xuân

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 có sẵn từ import static org.hamcrest.Matchers.*;

jsonPath có sẵn từ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

jsonPathtài liệu tham khảo có thể được tìm thấy ở đây


1
Tôi nhận được error: incompatible types: RequestMatcher cannot be converted to ResultMatcher cho.andExpect(content().contentType(contentType))
Ian Vaughan

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

23

Bộ @WithMockUserso containsStringkhớp mùa xuân của an ninh và hamcrest tạo nên một giải pháp đơn giản và thanh lịch:

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

Thêm ví dụ về github


4

Dưới đây là một ví dụ về cách phân tích phản hồi JSON và thậm chí cách gửi yêu cầu bằng bean ở dạng 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());
  }

Như bạn có thể thấy ở đây Booklà một yêu cầu DTO và UpdateBookResponselà một đối tượng phản hồi được phân tích cú pháp từ JSON. Bạn có thể muốn thay đổi ObjectMappercấu hình của Jakson .


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

Điều này sẽ cung cấp cho bạn cơ thể của phản ứng. "Tên người dùng đã được sử dụng" trong trường hợp của bạn.


giải thích ở đâu? nó là cần thiết hoặc bạn có thể đưa ra nhận xét loại câu trả lời này
user1140237

2

đây là một cách thanh lịch hơn

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

2

Bạn có thể sử dụng phương thức 'getContentAsString' để lấy dữ liệu phản hồi dưới dạng chuỗi.

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

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

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

Bạn có thể tham khảo liên kết này cho ứng dụng thử nghiệm.


1

Một cách tiếp cận có thể đơn giản là bao gồm gsonsự phụ thuộc:

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

và phân tích giá trị để thực hiện xác minh của bạ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!");
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.