17

I'm working a bit on an inherited code. I've written a test that is supposed to catch NullPointerException (for it is trying to call a method from null object)

@Test(expected=NullPointerException.class)
public void checkXRequirement_NullProduct_AddAction_ShouldThrowNullPointerException() throws CustomException {
  Site site = mock(Site.class);
  Product product = null;
  when(BasketHelper.getAction(request)).thenReturn(0);
  when(BasketHelper.getActionProduct(site, request)).thenReturn(product);
  BasketHelper.requiresX(request, site);

}

Relevant Methods and Variables:

public static final int ACTION_ADD = 0;
public static final int ACTION_DELETE = 1;

protected static int getAction(HttpServletRequest a_request) {
  String sBuyProduct = a_request.getParameter(ATTRIBUTE_NAME_BUY_PRODUCT);
  String sBuyProduct = a_request.getParameter(ATTRIBUTE_NAME_BUY_PRODUCT);

  if (sBuyProduct != null) iAction = ACTION_ADD;
  else (sDelProduct != null) iAction = ACTION_DELETE;

  return iBasketAction
}

protected static Product getActionProduct(Site a_site, HttpServletRequest a_request) {

    String sBuyProduct = a_request.getParameter(ATTRIBUTE_NAME_BUY_PRODUCT);
    String sDelProduct = a_request.getParameter(ATTRIBUTE_NAME_DEL_PRODUCT);
    String sProduct = null;

    switch (getBasketAction(a_request)) {
        case BASKET_ACTION_ADD:
        sProduct = sBuyProduct;
    break;
        case BASKET_ACTION_DELETE:
        sProduct = sDelProduct;
    break;
    }

    int iProductId;
    try {
        iProductId = Integer.parseInt(sProduct);
    } catch (NumberFormatException nbrEx) {
        return null;
    }

    Product prod = getProductById(iProductId);

    if (prod.isMasterProduct()) {
        prod = getChildProduct(prod, a_site, a_request);
    }

    return prod;
}


public static boolean requiresX(HttpServletRequest request, Site site) throws CustomException {
  try{
    if (getAction(request) == ACTION_ADD) { 
    Product prod = getActionProduct(site, request);
    return prod.getType().isRequiredX();
    }  
  } catch(NullPointerException exception) {
    log.error("Error Message", exception);
  }
  return false;
}

The jUnit result of running the test is a failure with the stack trace of:

java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<org.mockito.exceptions.misusing.WrongTypeOfReturnValue>
Caused by: org.mockito.exceptions.misusing.WrongTypeOfReturnValue: 
Integer cannot be returned by getParameter()
getParameter() should return String#

Do I misinterpret how when().thenReturn is supposed to work here? I just want getAction to return 0 and getActionProduct to return null whenever it's being called. Clearly getParameter() is called and I don't know why exactly.

Krzysztof Jarzyna
  • 227
  • 1
  • 3
  • 8
  • Can you show `getProduct()`? – Sotirios Delimanolis Aug 20 '13 at 16:18
  • In other news.... I can see why you would write this kind of test to replicate an issue. However, the real test here should be to verify that if the ````product```` is null the ````requiresX```` method is not called. Unless your inherited code is using the npe to control code flow? In which case - rip that out too. – BrantApps Aug 20 '13 at 16:40
  • I think you're right. changing the condition in requiresX method is probably a good idea. – Krzysztof Jarzyna Aug 20 '13 at 16:59

5 Answers5

16

Mockito cannot mock static method. Your when check is not valid:

  when(BasketHelper.getAction(request)).thenReturn(0);
  when(BasketHelper.getActionProduct(site, request)).thenReturn(product);

That is another reason why we want to reduce the use of static method as it is hard to mock.

There is no easier way to mock the behavior if your class stays like this. However if you want to change your design and make both methods non-static. The correct way of using "when" is to apply the check on mocked object. For example:

  BasketHelper basketHelper = mock(BasketHelper.class);
  when(basketHelper.getAction(request)).thenReturn(0);
  when(basketHelper.getActionProduct(site, request)).thenReturn(product);

But once again, this only work if you re-designed your class's getAction and getProduct method to be NON-STATIC.

I remember there are some other testing framework that does support mocking static method.

KKKCoder
  • 903
  • 9
  • 18
  • 4
    I think PowerMock allows for mocking static methods. Maybe I should look into it. Thanks – Krzysztof Jarzyna Aug 20 '13 at 16:29
  • Looks like it is :-) Good luck with it. – KKKCoder Aug 20 '13 at 16:31
  • You could look into PowerMock. Or you could refactor your code to avoid the use of static methods. I would recommend the latter. – Dawood ibn Kareem Aug 20 '13 at 18:59
  • Upvoted this response because of recommendation to move away from statics. To further drive home the point, please see Misko Hevery's article [Static Methods are Death to Testability](http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/). Basically, you need a 'seam' in order to inject the desired behavior...which statics do not allow (without something like PowerMock). Go to youtube for his talks has well! [link to playlist](http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PLED6CA927B41FF5BD) – Tony R Aug 20 '13 at 19:46
  • 3
    Also do not always rely on tools like PowerMock to 'force' a unit test. If you need a special tool like this, then there's probably a way to improve your design. In other words, try to refactor to unit test so you can test without PowerMock. If you are unable to do so (due to some constraint), feel free to use it. Takeaway being - just because PowerMock, or any similar tools, exist does NOT give you an excuse to write hard to test code! :) – Tony R Aug 20 '13 at 19:52
2

You can use PowerMock. First create mock of the class on which you are calling static methods -

mockStatic(BasketHelper.class);

Then define your stubs -

when(BasketHelper.getAction(request)).thenReturn(0);
when(BasketHelper.getActionProduct(site, request)).thenReturn(product);
Saurabh
  • 2,384
  • 6
  • 37
  • 52
0

This may help others who use annotations. If you are using annotations, may be you need to use @Mock instead of @InjectMocks. Because @InjectMocks works as @Spy and @Mock together. And @Spy keeps track of recently executed methods and you may feel that incorrect data is returned/subbed. Check these two:

https://groups.google.com/forum/?fromgroups#!topic/mockito/9WUvkhZUy90

http://code.google.com/p/mockito/issues/detail?id=127

dillip
  • 1,782
  • 17
  • 16
0

I ran across this thread while trying to fix the same issue in my tests.

In case others see this issue and end up here...In my case it was caused by not using the @PrepareForTest annotation for the support class.

John Dilley
  • 123
  • 8
0

Although it is not good coding practice to mock static method, private method or constructor. But if have some legacy code and you need to mock it. then in that case one can use Powermock with mockito.

Steps to follow when mocking static method using powermock.
1) use specific Runner i.e. PowerMockRunner.class.
2) use annotation @PrepareForTest(UtilityClass.class).
3) initialise utility class containing static method.
4) mock static method you want.

@RunWith(PowerMockRunner.class)
@PrepareForTest(Utility.class)
public class mockingStaticMethod(){

@Mock
Dependency dependency;

@InjectMocks
SystemUnderTest systemUnderTest;

@Test
public void testStaticMethod(){

    PowerMockito.mockStatic(Utility.class);
    When(Utility.staticMethod(arguments)).thenReturn(expectedValue);
    //systemUnderTest class uses the static method present in Utility class 
    //within the methodCallingStaticMethod()
    result = systemUnderTest.methodCallingStaticMethod();
    assertEquals(expectedValue, actualValue);


   }
}
Community
  • 1
  • 1
Ravi
  • 13
  • 5