Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot Test: UserControllerTest Fails When Using Jackson ObjectMapper To Convert Model To JSON String

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());
like image 416
Shahin Avatar asked Jan 20 '26 06:01

Shahin


1 Answers

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());
    }
}
like image 65
jlar310 Avatar answered Jan 23 '26 12:01

jlar310



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!