I am running a unit test on a rest controller method addUser() which executes successfully without JSON path. Anytime I try to use jsonPath to perform strict data assertion to verify the content of the JSON document, I get the error No value at JSON path "$.first_name". Any help on how to fix the issue will be much appreciated. Below is the code sample
@Entity
@Table(name = "users")
public class User extends IdBaseEntity{
@JsonProperty("first_name")
@Column(name = "first_name", nullable = false, length = 45)
@NotBlank(message = "First name cannot be blank")
@Length(min = 2, max = 45)
private String firstName;
@JsonProperty("last_name")
@NotBlank(message = "Last name cannot be blank")
@Length(min = 2, max = 45)
@Column(name="last_name", nullable = false, length = 45)
private String lastName;
@NotBlank(message = "Email cannot be blank")
@Length(min = 2, max = 45)
@Column(nullable = false, unique = true, length = 45)
private String email;
@NotBlank(message = "Password name cannot be blank")
@Length(min = 2, max = 45)
@Column(nullable = false, length = 45)
private String password;
@JsonProperty("phone_number")
@Column(length = 13)
private String phoneNumber;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name ="user_id"),
inverseJoinColumns = @JoinColumn(name="role_id")
)
private Set<Role> roles = new HashSet<>();
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Override
public String toString() {
return "User [firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + ", password=" + password
+ ", phoneNumber=" + phoneNumber + "]";
}
}
service interface
public interface UserService {
public User addUser(User user);
}
service class
@Service
public class UserServiceImpl implements UserService{
private UserRepository userRepo;
@Autowired
public UserServiceImpl(UserRepository userRepo) {
this.userRepo = userRepo;
}
@Override
public User addUser(User user) {
return userRepo.save(user);
}
}
restcontroller class
@RestController
@RequestMapping("/api/v1/users")
public class UserRestApiController {
private UserServiceImpl userService;
public UserRestApiController(UserServiceImpl userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity<User> addUser(@RequestBody @Valid User user){
User newUser = userService.addUser(user);
URI uri = URI.create("/api/v1/users");
return ResponseEntity.created(uri).body(newUser);
}
}
test class
@WebMvcTest(UserRestApiController.class)
public class UserRestApiControllerTest {
//end point uri
private static final String END_POINT_PATH = "/api/v1/users";
@MockBean
private UserServiceImpl userService;
@Autowired MockMvc mockMvc;
@Autowired ObjectMapper objectMapper;
@Test
public void testAddUserShouldReturn201Created() throws Exception {
User user = new User();
user.setFirstName("James");
user.setLastName("Kwarteng");
user.setEmail("james@gmail.com");
user.setPassword("23464433");
user.setPhoneNumber("04233455");
//fakes the UserServiceImpl class
Mockito.when(userService.addUser(user)).thenReturn(user);
//convert user object to json object
String bodyContent = objectMapper.writeValueAsString(user);
//perform http request
mockMvc.perform(post(END_POINT_PATH).contentType("application/json").content(bodyContent))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.first_name", is("James")))
.andDo(print());
}
}
error after running the test. part of the error code is omitted
java.lang.AssertionError: No value at JSON path "$.first_name"
at org.springframework.test.util.JsonPathExpectationsHelper.evaluateJsonPath(JsonPathExpectationsHelper.java:302)
at org.springframework.test.util.JsonPathExpectationsHelper.assertExistsAndReturn(JsonPathExpectationsHelper.java:326)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: java.lang.IllegalArgumentException: json can not be null or empty
at com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:401)
json can not be null or empty
. It doesn't work with jsonPath because the response body is always empty but the status is 201.
The Mockito.when...thenReturn
is not propagated in the controller class.
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Location:"/api/v1/users"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = /api/v1/users
Cookies = []
You need to involve Mockito by adding @ExtendWith(MockitoExtension.class)
instead of @WebMvcTest(UserRestApiController.class)
.
The second step is to build a MockMvc
instance by registering the UserRestApiController
@Controller instance.
Below is the fixed code that works.
@ExtendWith(MockitoExtension.class)
class UserRestApiControllerTest {
private static final String END_POINT_PATH = "/api/v1/users";
@InjectMocks
private UserRestApiController userRestApiController;
@Mock
private UserServiceImpl userService;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(userRestApiController).build();
}
@Test
void testAddUserShouldReturn201Created() throws Exception {
User user = new User();
user.setFirstName("James");
user.setLastName("Kwarteng");
user.setEmail("james@gmail.com");
user.setPassword("23464433");
user.setPhoneNumber("04233455");
//fakes the UserServiceImpl class
Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
//convert user object to json object
String bodyContent = new ObjectMapper().writeValueAsString(user);
//perform http request
mockMvc.perform(MockMvcRequestBuilders.post(END_POINT_PATH).contentType("application/json").content(bodyContent))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.first_name").value("James"))
.andExpect(jsonPath("$.last_name").value("Kwarteng"))
.andExpect(jsonPath("$.email").value("james@gmail.com"))
.andExpect(jsonPath("$.password").value("23464433"))
.andExpect(jsonPath("$.phone_number").value("04233455"))
.andDo(print());
}
}
the MockHttpServletResponse:
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Location:"/api/v1/users", Content-Type:"application/json"]
Content type = application/json
Body = {"email":"james@gmail.com","password":"23464433","first_name":"James","last_name":"Kwarteng","phone_number":"04233455"}
Forwarded URL = null
Redirected URL = /api/v1/users
Cookies = []