I'm currently trying to write Integration tests for a registration controller. When I run the test I get a different return value from the one I define using Mockito's when()
method.
@Test
public void whenValidInput_thenReturnsTrue() throws Exception{
// given
UserRegistrationRequest req = new UserRegistrationRequest("testUsername", "testPassword");
when(registrationService.registerUser(req)).thenReturn(true);
// when
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk())
.andReturn();
// then
assertThat(mvcResult.getResponse().getContentAsString())
.isEqualToIgnoringWhitespace("true");
}
org.junit.ComparisonFailure:
Expecting actual:
"false"
to be equal to:
"true"
when ignoring whitespace differences
Expected :"true"
Actual :"false"
I'm not sure why this is happening - although I feel like it is most likely due to a incorrect configuration. I set the mocks using openMocks
in my setup method - but it still won't return the defined value. Every method except for whenValidInput_thenReturnsTrue
will pass
, although I don't actually define mock return values for those methods and instead verify arguments using captors; this probably isn't something that's method-specific.
@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(value = RegistrationController.class)
public class RegistrationControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private RegistrationService registrationService;
@MockBean
private UserService userService;
@MockBean
private PasswordEncoder passwordEncoder;
public static final String URL = "/api/v1/register";
private AutoCloseable autoCloseable;
@Before
public void setUp() {
autoCloseable = MockitoAnnotations.openMocks(this);
}
@After
public void tearDown() throws Exception {
autoCloseable.close();
}
@Test
public void whenValidRegister_thenReturns200() throws Exception {
// given
UserRegistrationRequest req = new UserRegistrationRequest("testUsername", "testPassword");
mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk());
}
@Test
public void whenNullValueRegister_thenReturns400() throws Exception {
// given
UserRegistrationRequest req = new UserRegistrationRequest(null, "testPassword");
// when then
mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isBadRequest());
}
@Test
public void whenValidInput_thenMapsRegisterService() throws Exception {
// given
UserRegistrationRequest req = new UserRegistrationRequest("testUsername", "testPassword");
// when
mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk());
// then
ArgumentCaptor<UserRegistrationRequest> userArgumentCaptor = ArgumentCaptor.forClass(UserRegistrationRequest.class);
verify(registrationService).registerUser(userArgumentCaptor.capture());
assertThat(userArgumentCaptor.getValue().getUsername()).isEqualTo(req.getUsername());
assertThat(userArgumentCaptor.getValue().getPassword()).isEqualTo(req.getPassword());
}
@Test
public void whenValidInput_thenReturnsTrue() throws Exception{
// given
UserRegistrationRequest req = new UserRegistrationRequest("testUsername", "testPassword");
when(registrationService.registerUser(req)).thenReturn(true);
// when
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(req)))
.andExpect(status().isOk())
.andReturn();
// then
assertThat(mvcResult.getResponse().getContentAsString())
.isEqualToIgnoringWhitespace("true");
}
}
@CrossOrigin("http://localhost:4200") // Replace with proxy
@RequestMapping("/api/v1/")
@RestController
public class RegistrationController {
private final RegistrationService registrationService;
public RegistrationController(RegistrationService registrationService) {
this.registrationService = registrationService;
}
@PostMapping("/register")
@ResponseStatus(HttpStatus.OK)
public boolean registerUser(@Valid @RequestBody UserRegistrationRequest request) {
return registrationService.registerUser(request);
}
}
public class UserRegistrationRequest {
@NotNull private final String username;
@NotNull private final String password;
public UserRegistrationRequest(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "UserRegistrationRequest{"
+ "username='"
+ username
+ '\''
+ ", password='"
+ password
+ '\''
+ '}';
}
}
After changing the when
statement to this, the test will execute successfully.
when(registrationService.registerUser(Mockito.any(UserRegistrationRequest.class))).thenReturn(true);
After comparing the value passed to the registrationService
I got this output. The arguments appear to be the exact same.
Argument(s) are different! Wanted:
com.StruckCroissant.GameDB.registration.RegistrationService#0 bean.registerUser(
UserRegistrationRequest{username='testUsername', password='testPassword'}
);
-> at com.StruckCroissant.GameDB.registration.RegistrationControllerTest.whenValidInput_thenReturnsTrue(RegistrationControllerTest.java:130)
Actual invocations have different arguments:
com.StruckCroissant.GameDB.registration.RegistrationService#0 bean.registerUser(
UserRegistrationRequest{username='testUsername', password='testPassword'}
);
-> at com.StruckCroissant.GameDB.registration.RegistrationController.registerUser(RegistrationController.java:22)
As Lesiak pointed out, the UserRegistrationRequest
did not override the equals method. After adding the override for equals and hashCode to the class, the issue is resolved.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserRegistrationRequest that = (UserRegistrationRequest) o;
return username.equals(that.username) && password.equals(that.password);
}
@Override
public int hashCode() {
return Objects.hash(username, password);
}