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

17

我正在修改一些继承而来的代码。 我编写了一个测试,旨在捕获NullPointerException(因为它试图从空对象调用方法)。

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

}

相关方法和变量:

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#

我是否误解了 when().thenReturn 应该在这里起作用的方式? 我只想让 getAction 在被调用时返回 0,让 getActionProduct 返回 null。显然,getParameter() 被调用了,但我不知道为什么。


你能展示一下 getProduct() 吗? - Sotirios Delimanolis
另外,我可以理解为什么你会编写这种测试来复制问题。然而,真正的测试应该是验证如果“product”为空,则不调用“requiresX”方法。除非你继承的代码使用npe来控制代码流程?如果是这样的话,也要将其删除。 - BrantApps
我认为你是正确的。在 requiresX 方法中改变条件可能是个好主意。 - Krzysztof Jarzyna
5个回答

16

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方法为非静态方法时才起作用。

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


4
我认为PowerMock允许模拟静态方法。也许我应该研究一下它。谢谢。 - Krzysztof Jarzyna
看起来是的 :-) 祝你好运。 - KKKCoder
你可以考虑使用PowerMock。或者你可以重构你的代码,避免使用静态方法。我建议选择后者。 - Dawood ibn Kareem
3
不要总是依赖诸如PowerMock之类的工具来“强制”进行单元测试。如果你需要这样的特殊工具,那么很可能有改进设计的方法。换句话说,尝试重构以进行单元测试,这样就可以在不使用PowerMock的情况下进行测试。如果由于某些限制而无法这样做,则可以随意使用它。最重要的是 - 仅仅因为PowerMock或任何类似的工具存在并不意味着你有理由编写难以测试的代码! :) - Tony R

2

您可以使用PowerMock。首先创建一个类的模拟对象,该类包含您要调用静态方法的内容 -

mockStatic(BasketHelper.class);

然后定义你的存根 -
when(BasketHelper.getAction(request)).thenReturn(0);
when(BasketHelper.getActionProduct(site, request)).thenReturn(product);

0

0

在我的测试中,我遇到了与此相同的问题,并在尝试修复时发现了这个线程。

如果其他人遇到了这个问题并来到这里...在我的情况下,原因是没有为支持类使用@PrepareForTest注释。


0

虽然模拟静态方法、私有方法或构造函数不是良好的编码实践。但如果有一些遗留代码需要进行模拟,那么可以使用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);


   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接