如何使用mockMvc检查响应体中的String

pba*_*ski 214 java spring mocking spring-test-mvc

我有简单的集成测试

    @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(?);
}
Run Code Online (Sandbox Code Playgroud)

在最后一行中,我想将响应体中收到的字符串与期望的字符串进行比较

作为回应,我得到:

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
Run Code Online (Sandbox Code Playgroud)

尝试使用content(),body()但没有任何效果.

Sot*_*lis 316

您可以调用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
Run Code Online (Sandbox Code Playgroud)

  • @TimBüthe你能澄清一下吗?`@ RestController`表示所有处理程序方法都用`@ ResponseBody`隐式注释.这意味着Spring将使用`HttpMessageConverter`来序列化处理程序的返回值并将其写入响应.你可以用`content()`来获取身体. (7认同)
  • @SotiriosDelimanolis是正确的......我现在正在查看来自我的`@ RestController`-注释控制器的`getContentAsString()`返回的JSON. (5认同)

pba*_*ski 92

@Sotirios Delimanolis回答了这项工作,但我正在寻找比较这个mockMvc断言中的字符串

所以这就是

.andExpect(content().string("\"Username already taken - please try with different username\""));
Run Code Online (Sandbox Code Playgroud)

当然我的断言失败了:

java.lang.AssertionError: Response content expected:
<"Username already taken - please try with different username"> but was:<"Something gone wrong">
Run Code Online (Sandbox Code Playgroud)

因为:

  MockHttpServletResponse:
            Body = "Something gone wrong"
Run Code Online (Sandbox Code Playgroud)

所以这证明它有效!

  • 为了防止有人拥有动态ID的消息,就像我一样,知道string()方法也接受一个hamcrest*containsString*matcher:`.andExpect(content().string(containsString("\"Username)已经采取了");` (15认同)
  • @TimBüthe,这是不正确的.如果你有这样的问题你应该把它作为一个问题发布,因为这绝对不是预期的行为,也不是我在自己的代码中见过的行为. (4认同)
  • 请注意,导入是“org.hamcrest.Matchers.containsString()”。 (2认同)
  • 我还使用 org.hamcrest.Matchers.equalToIgnoringWhiteSpace() 匹配器来忽略所有空白字符。也许这对某人来说是有用的提示 (2认同)

ver*_*tti 58

Spring MockMvc现在直接支持JSON.所以你只要说:

.andExpect(content().json("{'message':'ok'}"));
Run Code Online (Sandbox Code Playgroud)

与字符串比较不同,它会说"缺少字段xyz"或"消息预期'确定'得到'nok'.

这个方法是在Spring 4.1中引入的.

  • 你能提供一个完整的例子吗?也不需要`ContentRequestMatchers`来支持该功能吗? (2认同)

Jer*_*emy 44

阅读这些答案,我可以看到很多与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));
Run Code Online (Sandbox Code Playgroud)

请求体只是一个json字符串,如果你愿意的话你可以从一个真正的json模拟数据文件中轻松加载,但是我没有在这里包含它,因为它会偏离问题.

返回的实际json看起来像这样:

{
    "data":"some value"
}
Run Code Online (Sandbox Code Playgroud)


Mic*_*l W 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.")));
}
Run Code Online (Sandbox Code Playgroud)

关于github的更多示例


use*_*759 23

摘自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")));
Run Code Online (Sandbox Code Playgroud)

is 可以从 import static org.hamcrest.Matchers.*;

jsonPath 可以从 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

jsonPath参考可以在这里找到

  • 我收到“错误:不兼容的类型:RequestMatcher 无法转换为 ResultMatcher” 的“.andExpect(content().contentType(contentType))” (2认同)

小智 8

here a more elegant way

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


sto*_*ito 8

这是一个如何解析 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());
  }
Run Code Online (Sandbox Code Playgroud)

正如您在这里看到的,这Book是一个请求 DTO,UpdateBookResponse是一个从 JSON 解析的响应对象。您可能想要更改 Jackson 的ObjectMapper配置。


Kor*_*gay 7

一种可能的方法是简单地包含gson依赖项:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

并解析值以进行验证:

@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();
        ResponseDto responseDto
                = new Gson().fromJson(responseBody, ResponseDto.class);
        Assertions.assertThat(responseDto.message).isEqualTo("hello world!");
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*oas 5

另一种选择是:

when:

def response = mockMvc.perform(
            get('/path/to/api')
            .header("Content-Type", "application/json"))

then:

response.andExpect(status().isOk())
response.andReturn().getResponse().getContentAsString() == "what you expect"
Run Code Online (Sandbox Code Playgroud)