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

Java Mockito when().thenReturn不必要地调用该方法,java,testing,junit,mockito,Java,Testing,Junit,Mockito,我正在处理一段继承的代码。 我编写了一个测试,它应该捕获NullPointerException(因为它试图从null对象调用一个方法) 相关方法和变量: public static final int ACTION_ADD = 0; public static final int ACTION_DELETE = 1; protected static int getAction(HttpServletRequest a_request) { String sBuyProduct = a_

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

相关方法和变量:

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;
}
运行测试的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#
java.lang.Exception:意外异常,应为,但为
原因:org.mockito.exceptions.misusing.ErrorTypeOfReturnValue:
getParameter()无法返回整数
getParameter()应返回字符串#

我是否误解了when().thenReturn在这里应该如何工作?我只希望getAction在调用时返回0,getActionProduct返回null。显然调用了getParameter(),我不知道确切的原因。

Mockito无法模拟静态方法。您的when检查无效:

  when(BasketHelper.getAction(request)).thenReturn(0);
  when(BasketHelper.getActionProduct(site, request)).thenReturn(product);
这是我们希望减少使用静态方法的另一个原因,因为它很难模拟

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

  BasketHelper basketHelper = mock(BasketHelper.class);
  when(basketHelper.getAction(request)).thenReturn(0);
  when(basketHelper.getActionProduct(site, request)).thenReturn(product);
但同样,只有将类的getAction和getProduct方法设计为非静态时,这才有效


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

您可以使用PowerMock。首先创建调用静态方法的类的mock-

mockStatic(BasketHelper.class);
然后定义存根-

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

这可能会帮助其他使用注释的人。如果您使用的是注释,则可能需要使用@Mock而不是@injectmock。因为@InjectMocks同时充当@Spy和@Mock。@Spy会跟踪最近执行的方法,您可能会觉得返回了不正确的数据。 检查以下两项:


我在尝试修复测试中的同一问题时遇到了此线程


以防其他人看到这个问题并在这里结束……在我的例子中,这是由于没有为支持类使用@PrepareForTest注释造成的。

尽管模拟静态方法、私有方法或构造函数不是很好的编码实践。但是如果你有一些遗留代码,你需要模仿它。在这种情况下,可以将Powermock与mockito一起使用

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


   }
}

您能否在其他新闻中显示
getProduct()
。。。。我明白为什么要编写这种测试来复制问题。然而,这里真正的测试应该是验证如果
产品
为空,则不会调用
requiresX
方法。除非继承的代码使用npe控制代码流?在这种情况下,把它也撕下来。我想你是对的。更改requiresX方法中的条件可能是个好主意。我认为PowerMock允许模拟静态方法。也许我应该调查一下。谢天谢地:-)祝你好运。你可以看看PowerMock。或者可以重构代码以避免使用静态方法。我会推荐后者。我对这个回答投了赞成票,因为我建议远离静态。为了进一步阐明这一点,请参阅Misko Hevery的文章。基本上,您需要一个“seam”来注入所需的行为……这是静态不允许的(没有类似于PowerMock的东西)。去youtube上听他的演讲吧!另外,不要总是依赖PowerMock之类的工具来“强制”单元测试。如果您需要这样的特殊工具,那么可能有一种方法可以改进您的设计。换句话说,尝试重构为单元测试,这样您就可以在没有PowerMock的情况下进行测试。如果您无法这样做(由于某些限制),请随意使用它。外卖——仅仅因为存在PowerMock或任何类似的工具,并不能给您一个编写难以测试的代码的借口!:)
@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);


   }
}