【问题标题】:Can Mockito stub a method without regard to the argument?Mockito 可以在不考虑参数的情况下存根方法吗?
【发布时间】:2011-08-23 14:25:21
【问题描述】:

我正在尝试使用 Mockito 测试一些遗留代码。

我想存根一个在生产中使用的FooDao,如下所示:

foo = fooDao.getBar(new Bazoo());

我会写:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

但明显的问题是 getBar() 从未使用我为方法存根的相同 Bazoo 对象调用。 (诅咒new 操作员!)

如果我能以不管参数如何都返回myFoo 的方式对方法进行存根,我会很高兴的。如果做不到这一点,我会听取其他解决方法的建议,但我真的很想避免更改生产代码,直到有合理的测试覆盖率。

【问题讨论】:

    标签: java unit-testing mocking mockito


    【解决方案1】:
    when(
      fooDao.getBar(
        any(Bazoo.class)
      )
    ).thenReturn(myFoo);
    

    或(避免nulls):

    when(
      fooDao.getBar(
        (Bazoo)notNull()
      )
    ).thenReturn(myFoo);
    

    别忘了导入匹配器(还有很多其他的可用):

    对于 Mockito 2.1.0 及更新版本:

    import static org.mockito.ArgumentMatchers.*;
    

    对于旧版本:

    import static org.mockito.Matchers.*;
    

    【讨论】:

    • 我喜欢答案在“接受答案冻结”结束之前。
    • 有一个 notNull(Bazoo.class) 就像 any(Bazoo.class) 一样(可能在这个答案的时候它不存在)
    • 我有一个稍微特殊的情况,我可以有两个可能的参数之一 - BazooCazoo,它们都是 Azoo 的子类。对于Bazoo,我需要返回foo,但对于Cazoo,我需要返回bar。在这种情况下,建议的Matchers.any() 解决方案不起作用,但是,Matchers.isA() 工作完美。
    • org.mockito.Matchers 现在已弃用 - 请改用org.mockito.ArgumentMatchers,即import static org.mockito.ArgumentMatchers.*(请参阅docs
    • when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
    【解决方案2】:

    http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

    anyObject() 应该满足您的需求。

    此外,您始终可以考虑为 Bazoo 类实现 hashCode()equals()。这将使您的代码示例按照您想要的方式工作。

    【讨论】:

    • 同意第二个建议,但出于非技术原因,我仍然选择不这样做。
    • Matchers 类已弃用(请参阅docs - “此类可能会在版本 3.0 中删除”
    • @JohannesRabauer 我猜应该改成any() ?
    • @golimar 看起来这完全一样。根据上面链接的文档,any()anyObject() 是相同的。然而完整的Matchers 类已被弃用,而应使用ArgumentMatchers 类。
    【解决方案3】:

    这样使用:

    when(
      fooDao.getBar(
        Matchers.<Bazoo>any()
      )
    ).thenReturn(myFoo);
    

    之前需要导入Mockito.Matchers

    【讨论】:

    • 这是贬义词!
    【解决方案4】:

    另一种选择是依靠老式的equals 方法。只要whenmock中的参数equals被测试代码中的参数,那么Mockito就会匹配mock。

    这是一个例子。

    public class MyPojo {
    
        public MyPojo( String someField ) {
            this.someField = someField;
        }
    
        private String someField;
    
        @Override
        public boolean equals( Object o ) {
            if ( this == o ) return true;
            if ( o == null || getClass() != o.getClass() ) return false;
            MyPojo myPojo = ( MyPojo ) o;
            return someField.equals( myPojo.someField );
        }
    
    }
    

    那么,假设您知道someField 的值是什么,您可以像这样模拟它。

    when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);
    

    优点:这比 any 匹配器更明确。作为一名代码审查者,我会留意初级开发人员编写的代码中的any,因为它会浏览他们的代码逻辑以生成正在传递的适当对象。

    con:有时传递给对象的字段是随机 ID。对于这种情况,您不能轻易地在模拟代码中构造预期的参数对象。

    另一种可能的方法是使用 Mockito 的 Answer 对象,该对象可与 when 方法一起使用。 Answer 允许您拦截实际调用并检查输入参数并返回一个模拟对象。在下面的示例中,我使用any 来捕获对被模拟方法的任何请求。但是然后在Answer lambda 中,我可以进一步检查 Bazo 参数......也许是为了验证是否将正确的 ID 传递给它。我更喜欢这个而不是 any 本身,以便至少对参数进行一些检查。

        Bar mockBar = //generate mock Bar.
    
        when(fooDao.getBar(any(Bazo.class))
        .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
            Bazo actualBazo = invocationOnMock.getArgument( 0 );
    
            //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
            return mockBar;
        } );
    

    总而言之,我喜欢依赖equals(预期参数和实际参数应该彼此相等)和如果相等是不可能的(由于无法预测实际参数的状态),我将求助于Answer 来检查参数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-13
      • 2012-02-23
      • 1970-01-01
      • 2013-08-22
      • 2014-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多