Search code examples
javaintellij-ideajunitmockitotestcase

Mockito: No such instance method


I'm writing testcases for below given method.

Method:

@Override
public void removeAllConnections(String uuid, String userName, String oimId) {
    customLogger.debug(Thread.currentThread().getStackTrace()[1].getMethodName(), userName, null, null, accessProviderBuilder.getUserName(), accessProviderBuilder.getUuid());

    UserAccessBean userAccessBean = new UserAccessBean(userName);
    userAccessBean.setOimid(oimId);
    userAccessBean.setToken("");
    log.info("removeAllConnections:oimid:"+userAccessBean.getOimId());
    UserProfileDetailBean userProfileDetail = accessClient.getAccess(userAccessBean,applicationForCsr);
    Set<AccountAccess> accountAccesses = userProfileDetail.getAccountAccessList();
    try {
        removeAllConnectionsExceptPrimary(oimId, userName, accountAccesses);
        removePrimaryConnection(oimId, userName, accountAccesses);
    } catch (ConnectionStateException e) {
        throw new ConnectionStateException(ConnectionNameNotRemoved, CONNECTION_REMOVAL_FAILED_MSG);
    } catch (InternalServerErrorException e) {
        throw new InternalServerErrorException(INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR_MSG);
    }
}

Below snippet is test case for given method.

Testcase:

@Test
public void testRemoveAllConnections() {
    UserAccessBean userAccessBean = new UserAccessBean(userName);
    when(accessClient.getAccess(userAccessBean,"CSR")).thenReturn(userProfileDetail);
    when(userProfileDetail.getAccountAccessList()).thenReturn(accountAccesses);
    String applicaionForCSR = "CSR";
    ReflectionTestUtils.setField(service, "applicationForCsr", applicaionForCSR);

    service.removeAllConnections(uuid, userName, oimId);

}

While debugging the code, my execution is failing at below given line as the value of userProfileDetail is null.

Set<AccountAccess> accountAccesses = userProfileDetail.getAccountAccessList();

While doing inspect element on accessClient.getAccess(userAccessBean,applicationForCsr) it is throwing below error. Pretty sure it is some silly mistake but unable to trace it.

Error:

No such instance method: 'UserProfileDetailBean v1.AccessService$$EnhancerByMockitoWithCGLIB$$a852895d.getAccess (UserAccessBean)'

Application: Spring Boot 1.5.0 Library: Mockito 2.7.X


Solution

  • I can suggest three possible solutions (or more like 2.5):

    a) Override the equals method of UserAccessBean, so that two UserAccessBeans are equal if and only if their names are equal. Of course, this might interfere with your productive code and I would not change the equals method only for testing.

    b) Since the username doesn't actually play a vital role in your test (the tests itself defines what the username is), you can simply ignore the details with...

    when(accessClient.getAccess(Mockito.any(UserAccessBean.class),Mockito.eq("CSR"))).thenReturn(userProfileDetail);
    

    This way, the userProfileDetail will be returned for any value of the first parameter. Of course, you lose detail here, so for example, the test would be correct if the username was somehow wrong, but chances are that this isn't possible in your test anyway.

    Mockito.any(...) is a so called matcher that tells Mockito to "use" this rule no matter what value is given for the parameter in question. Anything you put there is ok for Mockito. Mockito.eq("CSR") tells it, that this parameter must be equal to "CSR". So, the whole rule is...

    If someone calls accessClient.getAccess, no matter what the first parameter ist, but the 2nd must be equal to "CSR", then return userProfileDetail.

    So, with this, the first parameter can be anything. So, for example, the following to calls will be accepted:

    accessClient.getAccess(new UserAccessBean("correct name"), "CSR");
    accessClient.getAccess(new UserAccessBean("totally wrong name"), "CSR");
    

    ...because it does not matter what the first parameter is, ANY value will be accepted. So, what you "lose" there is the ability to check if the UserAccessBean is the correct one (because any is accepted). But in your case, since you only define those UserAccessBeans in the test anyway, this should not be a problem.

    But if it is, I can offer two workarounds...

    c) Use either a customer Matcher (that checks the name of the UserAccessBean) or use the Mockito.any(...) as above and an ArgumentCaptor to check if the name was correct in the end...

    ArgumentCaptor<UserAccessBean> captor = ArgumentCaptor.forClass(UserAccessBean.class);
    Mockito.verify(accessClient).getAccess(captor.capture(),Mockito.eq("CSR"));
    assertEquals(captor.getValue().getName(), "myName");