perfoming a Junit test with JsonPath not working

156 views Asked by At

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("[email protected]");
        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)
1

There are 1 answers

3
anicetkeric On BEST ANSWER

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("[email protected]");
        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("[email protected]"))
                .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":"[email protected]","password":"23464433","first_name":"James","last_name":"Kwarteng","phone_number":"04233455"}
    Forwarded URL = null
   Redirected URL = /api/v1/users
          Cookies = []