I'm testing a @RestContoller in Spring Boot which has a @PostMapping method and the method @RequestBody is validated using @Valid annotation. To test it, I'm using MockMvc and in order to populate request body content I'm using Jackson ObjectMapper; however, when the model is passed, the test fails:
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
User Model:
@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
UserController:
@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();
}
}
UserControllerTest:
@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", "[email protected]",
"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());
}
}
When I build JSON string manually, the test passes:
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\" : \"[email protected]\"\n" +
"}";
mockMvc.perform(MockMvcRequestBuilders.post("/api/user/register")
.content(json)
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk());
Spring is not necessarily providing you with a "vanilla" ObjectMapper instance. By having Spring inject the ObjectMapper instance into the test instead of creating an ObjectMapper with the default constructor, you will get an instance that matches your actual run-time environment, provided that your Spring profile for your unit tests is set up correctly.
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
public class UserControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Test
public void whenRequestValid_thenReturnStatusOk() throws Exception{
User user = new User("John", "QPublic", "[email protected]",
"123456789", LocalDateTime.now(), LocalDateTime.now());
mockMvc.perform(MockMvcRequestBuilders.post("/api/user/register")
.content(objectMapper.writeValueAsString(user))
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With