使用 Spring MockMVC 测试 Spring 的 @RequestBody
Posted
技术标签:
【中文标题】使用 Spring MockMVC 测试 Spring 的 @RequestBody【英文标题】:Testing Spring's @RequestBody using Spring MockMVC 【发布时间】:2013-12-28 13:20:43 【问题描述】:我正在尝试使用 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();
我正在测试的方法使用 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;
测试中gson创建的json:
"objectId":"33",
"userId":"4268321",
//... many more
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
所以我的问题是:如何使用 Spring MockMVC 测试此方法?
【问题讨论】:
您必须发布实际课程。使用 400,Spring 无法将您的请求正文转换为ObjectBean
对象。
感谢 Sotirios,这是我正在调查的日期格式问题。我正在传递一个 java.util 日期,而 gson 不喜欢它。
嗨,Sotirios。是的,不幸的是,这个问题仍然存在!我在这里问了一个关于日期格式的问题:***.com/questions/20509883/mysql-insert-gson-date
ObjectBean
类中日期字段的类型是什么? java.util.Date
、java.util.Calendar
还是 String
?还是其他?
【参考方案1】:
使用这个
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());
如 cmets 中所述,这是有效的,因为对象被转换为 json 并作为请求正文传递。此外,contentType 定义为 Json (APPLICATION_JSON_UTF8)。
More info on the HTTP request body structure
【讨论】:
@WendyG 因为对象被转换为json并作为请求体传递(另外,contentType定义为Json)。有关 HTTP 请求正文结构的更多信息:developer.mozilla.org/en-US/docs/Web/HTTP/Messages#Body @JonyD 我被告知之前没有在答案中注明评论作者,但很抱歉我弄错了你的名字。 为什么不直接使用 MediaType.APPLICATION_JSON? MediaType.APPLICATION_JSON_UTF8 也存在,但自 5.2 起已被弃用 这个修复对我有用:)【参考方案2】:以下对我有用,
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);
【讨论】:
【参考方案3】:问题是您正在使用自定义 Gson
对象序列化您的 bean,而应用程序正在尝试使用 Jackson ObjectMapper
(在 MappingJackson2HttpMessageConverter
内)反序列化您的 JSON。
如果你打开你的服务器日志,你应该会看到类似的东西
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"])
在其他堆栈跟踪中。
一种解决方案是将您的 Gson
日期格式设置为上述之一(在堆栈跟踪中)。
另一种方法是注册您自己的MappingJackson2HttpMessageConverter
,方法是将您自己的ObjectMapper
配置为与您的Gson
具有相同的日期格式。
【讨论】:
感谢 Sotirios,这是一个非常有用的课程。我曾假设反序列化将采用我列出的格式:yyyy-mm-dd-hh:mm:ss。这是因为@RequestBody 使用杰克逊吗?此外,这回答了我上面链接的另一个问题...... @MattB 涉及 2 个不同的进程。您正在使用您指定的格式进行序列化。您正在使用自己的格式的单独应用程序中反序列化。【参考方案4】:我在更新版本的 Spring 中遇到了类似的问题。我尝试使用new ObjectMapper().writeValueAsString(...)
,但它不适用于我的情况。
我实际上有一个 JSON 格式的 String
,但我觉得它实际上是将每个字段的 toString()
方法转换为 JSON。在我的例子中,日期LocalDate
字段最终会是:
"date":"year":2021,"month":"JANUARY","monthValue":1,"dayOfMonth":1,"chronology":"id":"ISO","calendarType" :"iso8601","dayOfWeek":"FRIDAY","leapYear":false,"dayOfYear":1,"era":"CE"
这不是发送请求的最佳日期格式...
最后,就我而言,最简单的解决方案是使用 Spring ObjectMapper
。它的行为更好,因为它使用 Jackson 来构建具有复杂类型的 JSON。
@Autowired
private ObjectMapper objectMapper;
我只是在测试中使用它:
mockMvc.perform(post("/api/")
.content(objectMapper.writeValueAsString(...))
.contentType(MediaType.APPLICATION_JSON)
);
【讨论】:
以上是关于使用 Spring MockMVC 测试 Spring 的 @RequestBody的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot WebFlux 测试未找到 MockMvc
使用 spring-data-jpa 和 MockMvc 进行 spring boot junit 测试
在 Spring Boot with Slice 中使用 MockMVC 测试静态内容
在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc
spring boot 测试无法注入 TestRestTemplate 和 MockMvc
使用 MockMvc 和 AutoConfigureMockMvc 测试 Spring Boot Web 应用程序时出现 LazyInitializationException 的原因是啥