如何使用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

使用content(),body()尝试了一些技巧,但没有任何效果。


19
就像建议一样,不应为返回400状态代码"Username already taken"。那应该更多是409冲突。
Sotirios Delimanolis

谢谢-此测试的目的是指定此类内容。
pbaranski

Answers:


356

您可以调用andReturn()并使用返回的MvcResult对象以获取内容String

见下文:

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 @RestController表示所有处理程序方法都用隐式注释@ResponseBody。这意味着Spring将使用a HttpMessageConverter来序列化处理程序的返回值并将其写入响应。你可以很容易得到身体content()
Sotirios Delimanolis 2014年

5
@SotiriosDelimanolis是正确的......我看现在的JSON返回的getContentAsString(),从我来到@RestController-annotated控制器。
保罗

我在错误消息中找到了要查找的内容:result.getResponse().getErrorMessage()
whistling_marmot

andReturn()返回空值
Giriraj,

@Giriraj andReturn返回MvcResult,如在指定的javadoc 这里
Sotirios Delimanolis

105

@Sotirios Delimanolis回答完成了这项工作,但是我一直在寻找在此模拟Mvc断言中比较字符串的方法

所以这是

.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的消息,很有帮助的是,要知道string()方法还接受了Hamcrest containsString匹配器:.andExpect(content().string(containsString("\"Username already taken");
molholm 2014年

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”或“消息,预期'确定'得到'确定'。

此方法在Spring 4.1中引入。


2
你能提供一个完整的例子吗?也不需要ContentRequestMatchers支持此功能吗?
Zarathustra 2015年

49

阅读这些答案,我可以看到很多与Spring 4.x相关的信息,出于各种原因,我正在使用3.2.0版本。因此,像json这样的事情content()就无法直接支持。

我发现使用MockMvcResultMatchers.jsonPath真的很容易,并且可以治疗。这是测试post方法的示例。

该解决方案的好处是您仍然可以匹配属性,而不依赖于完整的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(expectedData)))的
荣誉

28

摘自Spring的教程

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

Spring Security @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响应,甚至如何使用JSON形式的bean发送请求:

  @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解析的响应对象。您可能需要更改Jakson的ObjectMapper配置。


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

这应该使您得到响应的主体。您的情况为“用户名已被占用”。


哪里解释?它是必需的,或者您可以在评论中给予它这种类型的答案
user1140237'1

2

这是一种更优雅的方式

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

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!");
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.