Java Mockito when().thenReturn 不必要地调用该方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18340007/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:41:46  来源:igfitidea点击:

Mockito when().thenReturn calls the method unnecessarily

javatestingjunitmockito

提问by Krzysztof Jarzyna

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)

我正在研究继承的代码。我已经编写了一个应该捕获 NullPointerException 的测试(因为它试图从 null 对象调用一个方法)

@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:

运行测试的 jUnit 结果是失败的堆栈跟踪:

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.

我是否误解了 when().thenReturn 应该如何在这里工作?我只希望 getAction 返回 0 并且 getActionProduct 在调用时返回 null 。显然 getParameter() 被调用,我不知道为什么。

采纳答案by KKKCoder

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

Mockito 不能模拟静态方法。您的 when 支票无效:

  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:

如果你的班级保持这样,没有更简单的方法来模拟这种行为。但是,如果您想更改设计并使两种方法都非静态。使用“when”的正确方法是对模拟对象应用检查。例如:

  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.

但同样,这仅在您将类的 getAction 和 getProduct 方法重新设计为非静态时才有效。

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

我记得还有一些其他的测试框架支持模拟静态方法。

回答by Saurabh

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

您可以使用 PowerMock。首先创建您正在调用静态方法的类的模拟 -

mockStatic(BasketHelper.class);

Then define your stubs -

然后定义你的存根 -

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

回答by dillip pattnaik

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:

这可能会帮助其他使用注释的人。如果您使用注解,可能需要使用@Mock 而不是@InjectMocks。因为@InjectMocks 与@Spy 和@Mock 一起工作。并且@Spy 会跟踪最近执行的方法,您可能会觉得返回/替换了不正确的数据。检查这两个:

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

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

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

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

回答by John Dilley

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.

万一其他人看到这个问题并最终出现在这里......在我的情况下,这是由于没有对支持类使用@PrepareForTest 注释造成的。

回答by Ravi

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.

尽管模拟静态方法、私有方法或构造函数不是好的编码习惯。但是如果有一些遗留代码并且你需要模拟它。那么在这种情况下,可以将 Powermock 与 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.

使用 powermock 模拟静态方法时要遵循的步骤。
1) 使用特定的 Runner 即PowerMockRunner.class
2) 使用注解@PrepareForTest(UtilityClass.class)
3) 初始化包含静态方法的实用程序类。
4)模拟你想要的静态方法。

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


   }
}