【问题标题】:JUnit, assertEquals false when two objects seem exactly the same当两个对象看起来完全相同时,JUnit,assertEquals false
【发布时间】:2017-09-15 07:42:01
【问题描述】:

我正在测试一个 Spring 控制器,并模拟了我在其中调用的 loginService。

我在控制器响应的单元测试中遇到错误,但正如我在调试模式下看到的那样,比较的两个元素看起来完全一样。

您可以在下面看到显示两个元素相等的调试器图像。

现在让我们看看下面的代码。

被测系统(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;
}
...
}

测试类:

@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?!?!?!
    }
}

最后两个断言结果为假。当我调试这个测试时,我发现模拟似乎是正确的,而且在断言之前,被比较的两个对象看起来绝对相等。 JUnit给出的错误是这样的:

java.lang.AssertionError:预期:是 com.example.bytecode.SpringBootJWT.controllers.RestController$JsonResponseBody@587c290d

但是:是 com.example.bytecode.SpringBootJWT.controllers.RestController$JsonResponseBody@4516af24

您可以在下面看到调试器的图像。

【问题讨论】:

  • assertEquals() 不关心两个对象 A 和 B 看起来是否完全相同。它测试 A.equals(B) 是真还是假。所以它完全依赖于JsonResponseBody的equals()方法(如果有的话)的实现。但是请注意,您不是在测试两个对象的相等性。您正在测试 areTheyEquals 与 true 的相等性(用assertTrue(areTheyEquals) 表达会更清楚)。并且仅当您创建的新 responseBody 与响应的主体完全相同的对象(您正在与 == 进行比较)时,areTheyEquals 才设置为 true,这不可能是 true。
  • 您正在使用 == 比较对象
  • areTheyEquals 后来被添加,因为我无法理解为什么最后一个断言是错误的。我的问题是:如何调整最后一个断言(不是关于 areTheyEquals 的断言)?

标签: spring unit-testing junit mocking mockito


【解决方案1】:

此块中有两种不同类型的断言:

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

目前尚不清楚您的代码是否曾到达第二个断言 (assertThat(...)),但有几件事很清楚:

  • 使用== 断言相等性仅在您比较相同实例时才有效
  • 如果assertThat((RestController.JsonResponseBody) serverResponse.getBody(), is(responseBody)) 失败,则RestController.JsonResponseBody 上的equals() 方法认为这两个实例不同,因此:
    • RestController.JsonResponseBody 上的 equals() 方法没有按照您的预期进行
    • RestController.JsonResponseBody 的这两个实例完全不同
    • (很可能)RestController.JsonResponseBody 上没有 equals() 方法,因此它只是将 super 调用给仅评估对象相等性的父级

我建议:

  1. RestController.JsonResponseBody 上添加一个equals() 方法,或者只使用Mockito 的refEq() 来应用一个简单的、基于反射的相等检查
  2. 删除此断言:assertEquals(areTheyEquals, true);

【讨论】:

  • 第一个断言是后来添加的,因为我无法理解为什么最后一个断言不起作用。我没有 equals () 方法。我仍然不明白为什么他们被认为是不同的。真的一样!
  • @AlexMawashi 当您这样断言时:assertThat((RestController.JsonResponseBody) serverResponse.getBody(), is(responseBody)); 那么您是在要求 RestController.JsonResponseBody.equals() 决定它们是否“真的相同”,所以如果该断言没有通过,那么它们就是 不是 真的一样。如果您觉得它们对您的需求足够“相同”,请实施 RestController.JsonResponseBody.equals() 以使其符合您的期望。在您当前的职位上,您将其留给equals() 来决定它们是否相同,然后不同意其决定
猜你喜欢
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 2016-07-03
  • 2017-12-16
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
  • 2018-07-28
相关资源
最近更新 更多