Mocked service class returns false instead of defined value

255 views Asked by At

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 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");

}

Test output:

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.

RegistrationControllerTest

@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");

  }
}

RegistrationController

@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);
  }
}

UserRegistrationRequest

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
        + '\''
        + '}';
  }
}

Edit:

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)
1

There are 1 answers

0
StruckCroissant On BEST ANSWER

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.

Additions to UserRegistrationRequest

  @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);
  }