-1

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

Pratik Ambani
  • 2,523
  • 1
  • 18
  • 28
  • Please read [mcve] and enhance your question accordingly. Don't you think it matters how that null variable is declared / injected? – GhostCat Nov 25 '17 at 13:23
  • Thanks for your suggestion but null value itself is the part of my question. Inputs always welcomed, – Pratik Ambani Nov 25 '17 at 13:26
  • The UserAccessBean created in your removeAllConnections method, and the one created in your test method, are most probably not equal. So the mock accessClient.getAccess() method returns null for the UserAccessBean created in the removeAllConnections method. To explain better, you're basically doing, in your test, `when(accessClient.getAccess(user1) ...`, whereas in the code, there is a call to `accessClient.getAccess(someOtherDifferentUser, ...)`. – JB Nizet Nov 25 '17 at 13:26
  • @JBNizet Confirmed, values at both places are same. – Pratik Ambani Nov 25 '17 at 13:37
  • Confirmed how? Post the code you have executed to prove that the two UserAccessBean instances are equal. – JB Nizet Nov 25 '17 at 13:39
  • @JBNizet confirmed by debugging.. – Pratik Ambani Nov 25 '17 at 13:42
  • Debugging won't tell you if the two instances are equal. Do you understand what equality means? Testing if `user1.equals(user2)` is true will tell you. So, construct and modify a UserAccessBean as you're doing in the method. Construct and modify another one as you're doing in the test method, and test if `user1.equals(user2)` is true. – JB Nizet Nov 25 '17 at 13:44
  • With all due respect, you can check equality while debugging by reading method references of both the objects, which are same in my case. – Pratik Ambani Nov 25 '17 at 13:53
  • OK then. If you know better, good luck. – JB Nizet Nov 25 '17 at 14:38
  • @JBNizet Didn't mean to, but please accept my sincere apologies if I've offended you. I want you to help me understand the concept which I'm possibly unaware of. – Pratik Ambani Nov 25 '17 at 14:43
  • Then why don't you do hat I suggest you do? And answer my questions? – JB Nizet Nov 25 '17 at 14:44
  • To elaborate a little bit... For your mocking to work, the `equals` method of `UserAccessBean` must return `true` for both instances (the one given in the test at `when(...)` and the one later given to `accessclient`). If the `equals` method is not overriden in `UserAccessBean` (or also checks members like oimId, etc.), chances are, it will not return `true` and thus mockito will not recognize that this is what you were expecting. Depending on what you exactly need to check, there are multiple solutions. – Florian Schaetz Nov 26 '17 at 15:36
  • @FlorianSchaetz Thanks for your inputs. Can you please suggest how do I equalize them? because the second UserAccesSBean is initialized at runtime. – Pratik Ambani Nov 26 '17 at 15:38

1 Answers1

2

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");
Florian Schaetz
  • 10,454
  • 5
  • 32
  • 58
  • ...and "B" worked. Thank you so much. :) but not happy coz didn't understand how it worked. Would you please explain? "so for example, the test would be correct if the username was somehow wrong" – Pratik Ambani Nov 27 '17 at 13:18
  • I updated the answer with a little bit more detail, perhaps it helps. If not, feel free to ask for clarification. – Florian Schaetz Nov 27 '17 at 15:41