使用Spring MockMVC测试Spring的@RequestBody

Mat*_*att 47 integration-testing spring-mvc

我正在尝试使用Spring的MockMVC框架测试将对象发布到数据库的方法.我构建了如下测试:

@Test
public void testInsertObject() throws Exception { 

    String url = BASE_URL + "/object";

    ObjectBean anObject = new ObjectBean();
    anObject.setObjectId("33");
    anObject.setUserId("4268321");
    //... more

    Gson gson = new Gson();
    String json = gson.toJson(anObject);

    MvcResult result = this.mockMvc.perform(
            post(url)
            .contentType(MediaType.APPLICATION_JSON)
            .content(json))
            .andExpect(status().isOk())
            .andReturn();
}
Run Code Online (Sandbox Code Playgroud)

我正在测试的方法使用Spring的@RequestBody来接收ObjectBean,但测试总是返回400错误.

@ResponseBody
@RequestMapping(    consumes="application/json",
                    produces="application/json",
                    method=RequestMethod.POST,
                    value="/object")
public ObjectResponse insertObject(@RequestBody ObjectBean bean){

    this.photonetService.insertObject(bean);

    ObjectResponse response = new ObjectResponse();
    response.setObject(bean);

    return response;
}
Run Code Online (Sandbox Code Playgroud)

gson在测试中创建的json:

{
   "objectId":"33",
   "userId":"4268321",
   //... many more
}
Run Code Online (Sandbox Code Playgroud)

ObjectBean类

public class ObjectBean {

private String objectId;
private String userId;
//... many more

public String getObjectId() {
    return objectId;
}

public void setObjectId(String objectId) {
    this.objectId = objectId;
}

public String getUserId() {
    return userId;
}

public void setUserId(String userId) {
    this.userId = userId;
}
//... many more
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:如何使用Spring MockMVC测试此方法?

小智 66

使用这个

public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));

@Test
public void testInsertObject() throws Exception { 
    String url = BASE_URL + "/object";
    ObjectBean anObject = new ObjectBean();
    anObject.setObjectId("33");
    anObject.setUserId("4268321");
    //... more
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
    ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
    String requestJson=ow.writeValueAsString(anObject );

    mockMvc.perform(post(url).contentType(APPLICATION_JSON_UTF8)
        .content(requestJson))
        .andExpect(status().isOk());
}
Run Code Online (Sandbox Code Playgroud)

正如评论中所描述的那样,这是有效的,因为对象被转换为json并作为请求体传递.此外,contentType定义为Json(APPLICATION_JSON_UTF8).

有关HTTP请求正文结构的更多信息

  • 为什么不直接使用 MediaType.APPLICATION_JSON?MediaType.APPLICATION_JSON_UTF8 也存在,但自 5.2 起已被弃用 (2认同)

Vic*_*cky 11

以下对我有用

  mockMvc.perform(
            MockMvcRequestBuilders.post("/api/test/url")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(asJsonString(createItemForm)))
            .andExpect(status().isCreated());

  public static String asJsonString(final Object obj) {
    try {
        return new ObjectMapper().writeValueAsString(obj);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
Run Code Online (Sandbox Code Playgroud)


Joj*_*oes 9

我在最新版本的 Spring 中遇到了类似的问题。我尝试使用 anew ObjectMapper().writeValueAsString(...)但它在我的情况下不起作用。

我实际上有一个StringJSON 格式的,但我觉得它实际上是将toString()每个字段的方法转换为 JSON。就我而言,日期LocalDate字段最终将如下所示:

“日期”:{“年份”:2021,“月份”:“一月”,“月份值”:1,“月份日期”:1,“年表”:{“id”:“ISO”,“日历类型”:“iso8601 "},"dayOfWeek":"星期五","leapYear":false,"dayOfYear":1,"era":"CE"}

这不是在请求中发送的最佳日期格式......

最后,就我而言,最简单的解决方案是使用 Spring ObjectMapper. 它的行为更好,因为它使用 Jackson 来构建具有复杂类型的 JSON。

@Autowired
private ObjectMapper objectMapper;
Run Code Online (Sandbox Code Playgroud)

我只是在测试中使用了它:

mockMvc.perform(post("/api/")
                .content(objectMapper.writeValueAsString(...))
                .contentType(MediaType.APPLICATION_JSON)
);
Run Code Online (Sandbox Code Playgroud)


Sot*_*lis 5

问题在于,Gson当应用程序尝试使用Jackson ObjectMapper(在中MappingJackson2HttpMessageConverter)反序列化JSON时,您正在使用自定义对象序列化bean 。

如果打开服务器日志,应该会看到类似

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2013-34-10-10:34:31': not a valid representation (error: Failed to parse Date value '2013-34-10-10:34:31': Can not parse date "2013-34-10-10:34:31": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
 at [Source: java.io.StringReader@baea1ed; line: 1, column: 20] (through reference chain: com.spring.Bean["publicationDate"])
Run Code Online (Sandbox Code Playgroud)

在其他堆栈跟踪中。

一种解决方案是将Gson日期格式设置为上述格式之一(在stacktrace中)。

另一种方法是MappingJackson2HttpMessageConverter通过配置自己ObjectMapper的日期格式与自己的日期相同来注册自己的日期Gson