【问题标题】:Mockito when().thenReturn calls the method unnecessarilyMockito when().thenReturn 不必要地调用该方法
【发布时间】:2013-08-20 16:11:55
【问题描述】:

我正在研究继承的代码。 我编写了一个应该捕获 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);

}

相关方法和变量:

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()吗?
  • 在其他新闻中......我明白你为什么要编写这种测试来复制问题。但是,这里真正的测试应该是验证如果product 为空,则不调用requiresX 方法。除非您继承的代码使用 npe 来控制代码流?在这种情况下 - 也撕掉它。
  • 我认为你是对的。更改 requiresX 方法中的条件可能是个好主意。

标签: java testing junit mockito


【解决方案1】:

Mockito 不能模拟静态方法。您的何时检查无效:

  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 方法重新设计为 NON-STATIC 时才有效。

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

【讨论】:

  • 我认为 PowerMock 允许模拟静态方法。也许我应该调查一下。谢谢
  • 看起来是 :-) 祝你好运。
  • 你可以看看 PowerMock。或者您可以重构代码以避免使用静态方法。我会推荐后者。
  • 赞成此回复,因为建议远离静态。要进一步说明这一点,请参阅 Misko Hevery 的文章 Static Methods are Death to Testability。基本上,您需要一个“接缝”来注入所需的行为......这是静态不允许的(没有类似 PowerMock 的东西)。去youtube看他的演讲很好! link to playlist
  • 也不要总是依赖像 PowerMock 这样的工具来“强制”进行单元测试。如果您需要这样的特殊工具,那么可能有一种方法可以改进您的设计。换句话说,尝试重构为单元测试,这样您就可以在没有 PowerMock 的情况下进行测试。如果您无法这样做(由于某些限制),请随意使用它。外卖 - 仅仅因为 PowerMock 或任何类似工具的存在并不会给您提供编写难以测试的代码的借口! :)
【解决方案2】:

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

mockStatic(BasketHelper.class);

然后定义你的存根 -

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      我在尝试修复测试中的相同问题时遇到了这个线程。

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-05
          • 1970-01-01
          • 1970-01-01
          • 2020-02-23
          • 2017-07-21
          • 1970-01-01
          • 2016-01-12
          相关资源
          最近更新 更多