Mockito when().thenReturn calls the method unnecessarily

倾然丶 夕夏残阳落幕 提交于 2020-07-18 03:39:08

问题


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.


回答1:


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.




回答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);



回答3:


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




回答4:


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.




回答5:


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);


   }
}


来源:https://stackoverflow.com/questions/18340007/mockito-when-thenreturn-calls-the-method-unnecessarily

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!