我在Sping Boot 中测试了一个@RestContoller
,它有一个@PostMapping
方法,并且使用@Valid
annotation验证了方法@RequestBody
。为了测试它,我使用了MockMvc
,为了填充请求正文内容,我使用了Jackson ObjectMapper
;然而,当模型通过时,测试失败:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/user/register
Parameters = {}
Headers = [Content-Type:"application/json"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.springboottutorial.todoapp.controller.UserController
Method = public org.springframework.http.ResponseEntity<java.lang.String> com.springboottutorial.todoapp.controller.UserController.register(com.springboottutorial.todoapp.dao.model.User)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :200
Actual :400
用户模型:
@Entity
@Table(name = "users",
uniqueConstraints = @UniqueConstraint(columnNames = {"EMAIL"}))
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private long id;
@Column(name = "FIRST_NAME")
@NotNull
private String firstName;
@Column(name = "LAST_NAME")
@NotNull
private String lastName;
@Column(name = "EMAIL")
@NotNull
@Email
private String emailAddress;
@Column(name = "PASSWORD")
@NotNull
private String password;
@Column(name = "CREATED_AT")
@NotNull
@Convert(converter = LocalDateTimeConveter.class)
private LocalDateTime createdAt;
@Column(name = "UPDATED_AT")
@NotNull
@Convert(converter = LocalDateTimeConveter.class)
private LocalDateTime updatedAt;
public User(@NotNull String firstName, @NotNull String lastName,
@NotNull @Email String emailAddress, @NotNull String password,
@NotNull LocalDateTime createdAt, @NotNull LocalDateTime updatedAt) {
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
this.password = password;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
//setters and getters: omitted
用户控制器:
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
UserService userService;
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody @Valid User user){
userService.createUser(user);
return ResponseEntity.ok().build();
}
}
用户控制器测试:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
public class UserControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void whenRequestValid_thenReturnStatusOk() throws Exception{
User user = new User("John", "QPublic", "john.public@gmail.com",
"123456789", LocalDateTime.now(), LocalDateTime.now());
mockMvc.perform(MockMvcRequestBuilders.post("/api/user/register")
.content(new ObjectMapper().writeValueAsString(user))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
当我手动构建JSON字符串时,测试通过:
String json = "{\n" +
"\t\"firstName\" : \"John\",\n" +
"\t\"lastName\" : \"QPublic\",\n" +
"\t\"password\" : \"123456789\",\n" +
"\t\"createdAt\" : \"2016-11-09T11:44:44.797\",\n" +
"\t\"updatedAt\" : \"2016-11-09T11:44:44.797\",\n" +
"\t\"emailAddress\" : \"john.public@gmail.com\"\n" +
"}";
mockMvc.perform(MockMvcRequestBuilders.post("/api/user/register")
.content(json)
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk());
4条答案
按热度按时间s4n0splo1#
Spring并不一定为您提供一个“vanilla”ObjectMapper示例。通过让Spring将
ObjectMapper
示例注入到测试中,而不是使用默认构造函数创建ObjectMapper
,您将获得一个与实际运行时环境匹配的示例,前提是您的单元测试的Spring配置文件设置正确。roejwanj2#
正如chrylis在评论中提到的,问题的发生是由于Java 8 Date & Time API和Jackson序列化冲突。默认情况下,
ObjectMapper
不理解LocalDateTime
数据类型,所以我需要添加com.fasterxml.jackson.datatype:jackson-datatype-jsr310
依赖到我的maven,这是一个数据类型模块,使Jackson识别Java 8日期和时间API数据类型。一个stackoverflow question和一个blog post帮助我弄清楚了我的问题到底是什么。yrefmtwq3#
如果
LocalDateTime
不是问题,我认为我们应该在User类中实现equals
。zour9fqk4#
这是我如何实现我的休息控制器测试。也许它可以帮助你。
我使用这个抽象类来封装与 JSON Map相关的常见测试功能。
你可以在你的测试类中使用它,就像这样
我希望这对你有用