0

I'm trying to write a unit test for a POST REST endpoint. I need to mock a service method which is using in above endpoint using mockito. Using mock mvc to trigger the endpoint.

I'm mocking the userService.saveUser(user) using in the endpoint and return a integer as the created userId. But it seems like the mock is always returning 0 instead 7777 (userId deifined). For the "verify" it says "Argument(s) are different!" which is passing to userService(user) in endpoint and in the mock I have defined.

Mockito verify error:

Argument(s) are different! Wanted:
userService.saveUser(
    com.foo.dto.User@741f8dbe
);
-> at com.foo.controller.UserControllerTest.saveUser(UserControllerTest.java:104)
Actual invocation has different arguments:
userService.saveUser(
    com.foo.dto.User@212dfd39
);
-> at com.foo.controller.UserController.saveUser(UserController.java:45)

Comparison Failure:  <Click to see difference>

I guess this is happening because @RequestBody annotation creates a whole new object of User. That causes issue of not working the "userService" mock.(correct me If I'm wrong)

May be I have to do a work around to trigger the endpoint and pass the "user" object to the "userService" as it is defined in Test class and skip the @RequestBody annotation.

Can anyone guide me to get through this? any help would be highly appreciated.

Endpoint:

@PostMapping("/users")
public ResponseEntity<String> saveUser(@RequestBody User user){

    int userId = userService.saveUser(user);

    return new ResponseEntity<>("User created. Id: "+ userId, HttpStatus.CREATED);
}

Test Class:

@RunWith(SpringJUnit4ClassRunner.class)
public class UserControllerTest {
    @InjectMocks
    private UserController userController;
    @Mock
    private UserService userService;
    private ObjectMapper mapper;
    private MockMvc mockMvc;
    private Integer userId = 7777;

    @Before
    public void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
        mapper = new ObjectMapper();

        user = new User();
        user.setName("DummyUser");
    }

    @Test
    public void saveUser(){
        when(userService.saveUser(user)).thenReturn(userId);
        this.mockMvc.perform(post("/users")
                .accept(MediaType.TEXT_PLAIN)
                .contentType(MediaType.APPLICATION_JSON)
                .content(mapper.writeValueAsString(user)))
                .andExpect(status().isCreated())
                .andExpect(content()
                       .contentTypeCompatibleWith(MediaType.TEXT_PLAIN))
                .andExpect(content().string("User created. Id: "+userId));
        verify(userService).saveUser(user);
    }

}
wannix
  • 120
  • 1
  • 2
  • 12

1 Answers1

1

I think the reason is the one you said. What you could do is using a matcher to pass to your mock so it is going to return what you want as long as it is receiving an object of a specific type (instead of a specific object:

import static org.mockito.ArgumentMatchers.any;
...
when(userService.saveUser(any(User.class))).thenReturn(userId);

The mock, used as you are using it, it is going to return what you want only if it is receiving as parameter of the call the specific object you are passing when doing when. As you can see from the exception you have, the mock is expecting to receive an object with reference com.foo.dto.User@741f8dbe (which is the one you are creating in your setUp method), but it is receiving com.foo.dto.User@212dfd39 which is the one generated by Jackson when it deserializes the body of the rest call.

burm87
  • 768
  • 4
  • 17