Alessandro Argentieri
Alessandro Argentieri

Reputation: 3215

JUnit, assertEquals false when two objects seem exactly the same

I'm testing a Spring controller and I've mocked the loginService which I call in it.

I got errors in Unit Testing on the response of the controller but, as I can see in debug mode, the two elements compared seem exactly the same.

Below you can see the image of the debugger that shows that the two elements are equals.

Now let's see the code below.

System Under Test (RestController.class)

@org.springframework.web.bind.annotation.RestController
public class RestController {

@Autowired
LoginService loginService;  
...
//METHOD BEING TESTED
@RequestMapping(value = "/login", method = POST)
public ResponseEntity<JsonResponseBody> loginUser(@RequestParam(value ="id") String id, @RequestParam(value="password") String pwd){
    try {
        Optional<User> userr = loginService.getUserFromDbAndVerifyPassword(id, pwd);      //verify the presence into the database
        if (userr.isPresent()) {
            User user = userr.get();                                  //get the User from the optional got from the DB
            String jwt = loginService.createJwt(user.getId(), user.getUsername(), user.getPermission(), new Date());
            //set the jwt token into the header of response
            return ResponseEntity.status(HttpStatus.OK).header("jwt", jwt).body(new JsonResponseBody(HttpStatus.OK.value(),"Success! User logged in." + jwt));
        }
    }catch(UserNotLoggedException e1){ //thrown by loginService#getUserFromDbAndVerifyPassword
        return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new JsonResponseBody(HttpStatus.FORBIDDEN.value(),"Login failed! Wrong credentials. " + e1.toString()));
    }catch(UnsupportedEncodingException e2){  //thrown by loginService#createJwt
       return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new JsonResponseBody(HttpStatus.FORBIDDEN.value(),"Login failed! Encoding permission token error. " + e2.toString()));
    }
    //send response to client
    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new JsonResponseBody(HttpStatus.FORBIDDEN.value(),"Login failed! No corrispondence found into the database of users."));
}

...
//INNER CLASS USING LOMBOK
@AllArgsConstructor
public class JsonResponseBody{
    @Getter @Setter
    private int server;
    @Getter @Setter
    private Object response;
}
...
}

Test class:

@RunWith(MockitoJUnitRunner.class)
public class RestControllerTest {

   @InjectMocks
   RestController restController;

   @Mock
   LoginService loginService;

   @Test
   public void loginUserWithSuccessTest() throws UserNotLoggedException, UnsupportedEncodingException{
       User user = new User("BDAGPP32E08F205K", "Pippo Baudo", "ILoveSanRemoEncrypted", "conduttore");
       Optional<User> fakeUserOptional = Optional.of(user);
       when(loginService.getUserFromDbAndVerifyPassword("BDAGPP32E08F205K","ILoveSanRemo")).thenReturn(fakeUserOptional);

       String jwt = "aaaa.bbbb.cccc";
       when(loginService.createJwt(eq(user.getId()), eq(user.getUsername()), eq(user.getPermission()), any(Date.class))).thenReturn(jwt);

       ResponseEntity serverResponse = restController.loginUser("BDAGPP32E08F205K","ILoveSanRemo");
       RestController.JsonResponseBody responseBody = restController.new JsonResponseBody(HttpStatus.OK.value(), "Success! User logged in." + jwt);

       assertEquals(serverResponse.getStatusCode(), HttpStatus.OK); //TRUE

       boolean areTheyEquals=false;
       if((RestController.JsonResponseBody) serverResponse.getBody() == responseBody){
           areTheyEquals = true;
       }
       assertEquals(areTheyEquals, true); //FALSE?!?!?!?!?
       assertThat((RestController.JsonResponseBody) serverResponse.getBody(), is(responseBody)); //FALSE?!?!?!
    }
}

The last two asserts result false. When I debug this test I discover how the mock seems to be correct and also, before the asserts the two objects being compared seem absolutely equals. The error given by JUnit is this:

java.lang.AssertionError: Expected: is com.example.bytecode.SpringBootJWT.controllers.RestController$JsonResponseBody@587c290d

but: was com.example.bytecode.SpringBootJWT.controllers.RestController$JsonResponseBody@4516af24

Below you can see the image of the debugger.

The Debugger Tab shows how the two elements being compared (serverResponse.body and responseBody) seem exactly equal. But the last two asserts are false

Upvotes: 0

Views: 3606

Answers (1)

glytching
glytching

Reputation: 47895

There are two different types of assertion in this block:

boolean areTheyEquals=false;
if((RestController.JsonResponseBody) serverResponse.getBody() == responseBody){
    areTheyEquals = true;
}
assertEquals(areTheyEquals, true); //FALSE?!?!?!?!?
assertThat((RestController.JsonResponseBody) serverResponse.getBody(), is(responseBody)); //FALSE?!?!?!

It's unclear whether your code ever reaches the second assertion (assertThat(...)) but a few things are clear:

  • Asserting equality using == will only work if you are comparing the same instance
  • If assertThat((RestController.JsonResponseBody) serverResponse.getBody(), is(responseBody)) fails then the equals() method on RestController.JsonResponseBody has deemed these two instances to be different so either:
    • The equals() method on RestController.JsonResponseBody does not do what you expect it to do
    • These two instances of RestController.JsonResponseBody are genuinely different
    • (Most likely) There is no equals() method on RestController.JsonResponseBody so it is just calling super to a parent which just evaluates object equality

I would suggest:

  1. Add an equals() method on RestController.JsonResponseBody or just use Mockito's refEq() to apply a simple, reflection based equality check
  2. Remove this assertion: assertEquals(areTheyEquals, true);

Upvotes: 1

Related Questions