如何使用mockMvc检查响应体中的字符串

问题描述 投票:0回答:15

我有简单的集成测试

@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() 的一些技巧,但没有任何效果。

java spring mocking spring-test-mvc
15个回答
549
投票

您可以调用

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 

173
投票

@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"

所以这证明它有效!


97
投票

Spring MockMvc 现在直接支持 JSON。所以你只需说:

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

与字符串比较不同,它会说“缺少字段 xyz”或“消息预期‘ok’得到‘nok’。

该方法是Spring 4.1引入的。


77
投票

阅读这些答案,我可以看到很多与 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"
}

48
投票

摘自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
参考可以在这里

找到

30
投票

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上有更多示例


12
投票

这里有一个更优雅的方式

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

12
投票

一种可能的方法是简单地包含

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

9
投票

这里是一个如何解析 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 解析的响应对象。您可能想要更改 Jackson 的
ObjectMapper
配置。


7
投票

另一种选择是:

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"

4
投票

另一个例子是:

.andExpect(jsonPath("$").value(containsString("You have successfully deleted")));

身体反应:

Body = You have successfully deleted a [Object] with ID: 1


3
投票

您可以使用

getContentAsString
方法获取字符串形式的响应数据。

    String payload = "....";
    String apiToTest = "....";
    
    MvcResult mvcResult = mockMvc.
                perform(post(apiToTest).
                content(payload).
                contentType(MediaType.APPLICATION_JSON)).
                andReturn();
    
    String responseData = mvcResult.getResponse().getContentAsString();

您可以参考此链接进行测试申请。


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

这将为您提供响应的正文。在您的情况下,“用户名已被占用”。


1
投票

这是一种更适合生产的方法,如果您可能有很大的 json 响应,那么您不必使用 json 字符串弄乱您的测试文件,只需从静态资源文件夹加载它们并直接断言它们。

  @Test
  @DisplayName("invalid fields")
  void invalidfields() throws Exception {

    String request = getResourceFileAsString("test-data/http-request/invalid-fields.json");
    String response_file_path = "test-data/http-response/error-messages/invalid-fields.json";
    String expected_response = getResourceFileAsString(response_file_path);

    mockMvc.perform(evaluateRulesOnData(TRACKING_ID.toString(), request))
        .andExpect(status().isBadRequest())
        .andExpect(content().json(expected_response));
  }

从类路径加载测试文件的帮助函数

  public static String getResourceFileAsString(String fileName) throws IOException {
    Resource resource = new ClassPathResource(fileName);
    File file = resource.getFile();
    return new String(Files.readAllBytes(file.toPath()));
  }

预期响应有一个数组,列表中包含许多元素,尽管在每次测试运行期间都是随机顺序,但这些元素都是匹配的。


0
投票

如果您的响应是 JSON 并且它不是那么小,您可以执行以下操作。

创建实用程序类:

@UtilityClass
public class FileUtils {
    public String readFromFile(String fileName) throws IOException {
        String filePath = String.format("responses/%s", fileName);
        File resource = new ClassPathResource(filePath).getFile();
        byte[] byteArray = Files.readAllBytes(resource.toPath());
        return new String(byteArray);
    }
}

并直接从您的测试中使用它,例如:

@Test
void getAllDepartments() throws Exception {
    mockMvc.perform(get("/v1/departments")
                    .header(AUTHORIZATION_HEADER, generateToken())
                    .contentType(MediaType.APPLICATION_JSON_VALUE))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(content().json(FileUtils.readFromFile("departments-response.json")));
}

其中

departments-response.json
是:

[
  {
    "id": 1,
    "name": "Gryffindor"
  },
  {
    "id": 2,
    "name": "Hufflepuff"
  }
]
© www.soinside.com 2019 - 2024. All rights reserved.