3年前 (2021-08-30)  Spock系列 |   7 条评论  5923 
文章评分 3 次,平均分 5.0
[收起] 文章目录

Mock

按照Spock官方文档(https://spockframework.org/spock/docs/2.0/interaction_based_testing.html)的定义:

Spock中Mock()、Stub()、Spy()方法的区别

描述规范下的对象与其合作者之间(强制)交互的行为。

说人话就是Mock()的对象是一个虚拟类,用于替换真实的类,为每个方法调用返回一个默认值:引用类型是null,基本类型为 0false,比如可以把调用其他服务的接口、数据库访问、Redis等资源的访问给模拟掉,返回我们事先准备好的假数据。

目前文章里大部分代码示例使用的都是Mock()这个方法,模拟的优点是可以将被测类与系统的其余部分隔离掉,不会进行真实的调用。

Spock的Mock方法不仅可以模拟方法返回结果,还可以模拟方法行为 ,比如之前的文章中介绍的验证某个方法的调用次数:

Spock中Mock()、Stub()、Spy()方法的区别

2 * moneyDAO.getExchangeByCountry(_) >> 0.1413 >> 0.1421

除了设置getExchangeByCountry()返回模拟的值:0.14130.1421,还要验证该方法被调用了2次,最前面的2就表示调用次数,如果没有调用2次则单测会失败。更多行为模拟可以参考官方文档的示例代码:

Spock中Mock()、Stub()、Spy()方法的区别

Stub

Spock中Mock()、Stub()、Spy()方法的区别

存根是使协作者以某种方式响应方法调用的行为。当存根方法时,你不关心该方法是否以及将被调用多少次;你只是希望它在被调用时返回一些值

Stub()存根方法也是一个虚拟类,比Mock()方法更简单一些,只返回事先准备好的假数据,而不提供交互验证(即该方法是否被调用以及将被调用多少次)。使用存根Stub只能验证状态(例如测试方法返回的结果数据是否正确,list大小等,是否符合断言)。

所以Mock比Stub的功能更多一些,但如果我们只是验证结果使用Stub就足够了,用法和Mock一样,而且更轻量一些。

Spy

Spock中Mock()、Stub()、Spy()方法的区别

间谍总是基于真实的对象,对间谍的方法调用会自动委托给真实对象。

Spy()间谍或叫刺探方法,他会包装一个真实的对象,默认情况下将调用真实的方法,在Spock中 Spy 也具有Mock的能力。

举个例子,比如被测试的方法中会调用当前类的另外一个方法,但另外一个方法里面的逻辑很复杂或调用了外部依赖,那么我们可以通过Spy可以让第一个方法内部不调用另外一个方法,这样就只测试第一个方法,代码如下:

// 被测试代码
class MyClass {
    int methodA(int i){
        int j = methodB();
        // 其他业务逻辑...
        if (j > 0) {
            return Math.max(i, j);
        } else {
            return Math.min(i, j);
        }
    }
 
    int methodB(){
        System.out.println("methodB call");
        return 2; // 结果假如是从查询数据库或其他接口获取
    }
}
 
// 单元测试
class MyClassTest extends Specification {
    def "testMethodA"() {
        given:
        def spy = Spy(MyClass)
        1 * spy.methodB() >> 2
 
        when:
        def result = spy.methodA(-1)
 
        then:
        result > 0
    }
}

运行上面的单测结果显示是不会输出"methodB call"的,因为methodB()方法已经被mock了,虽然是在同一个类的methodA()方法里有调用methodB(),但也不会被真正被调用。

这样看Spy()的功能最强大,但就像官方文档说的:使用前请三思!因为一般这样做就意味着你的代码有坏味道,设计上可能有问题。

所以我们平时使用Mock就足够了。

  
 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2283.html

关于

发表评论

表情 格式
  1. 大佬,能写一个 如何 mock ClassA中 一个方法call() 中使用 resttemplate 的例子吗?

    spock小弟 评论达人 LV.1 2年前 (2021-11-16) [0] [0]
    • @spock小弟你是要mock掉http的rest模板请求吗?我理解就是正常的mock就行了,如果有什么问题可以提供下代码,我帮你看下

      sofia 博 主 2年前 (2021-11-21) [0] [0]
  2. 你的解答对我很有帮助,多谢!

    暗夜有魅 评论达人 LV.1 3年前 (2021-09-26) [0] [0]
  3. 你好,请教一下,可以使用Spock模拟Http请求吗?类似于Spring的MockMvc

    暗夜有魅 评论达人 LV.1 3年前 (2021-09-24) [0] [0]
    • @暗夜有魅这个感觉跟用什么单测框架无关,目前我的文章都是基于单元测试,即当前类中的某个方法测试,所以不关心是不是http的请求,比如一般项目中都会封装个HttpUtils的工具类,如果你的代码中有调用HttpUtils的方法,直接mock掉就行了。

      sofia 博 主 3年前 (2021-09-25) [0] [0]
  4. spy案例中被测方法里调用多个外部方法,这种场景似乎很常见。
    请教一下,为什么会有坏味道呢?
    或许这样会导致跨层测试?

    guodage 评论达人 LV.1 3年前 (2021-09-14) [0] [0]
    • @guodage迟复为歉!如文中所述Spy的例子:methodA内部调用了methodB,但这两个方法属于同一个类MyClass,所以不存在跨层调用(当然如果是跨层调用的话建议使用mock或stub避免,否则就超出单元测试的范畴了,这一点你说的对),我举的Spy场景是说,同一个类中,我要测试methodA方法,但methodA内部调用了methodB,而我又不想测试methodB,这种场景。

      这种场景为什么有坏味道呢?因为我觉得正常情况你测试了methodA方法,那么methodA方法内部调用的当前类的其他方法(比如methodB)都应该被测试到,这样才能保证单测的覆盖率,除非说之前已经有一个方法比如methodC内部已经测试过methodB了(但这种情况下其实也没必要spy掉methodB

      sofia 博 主 3年前 (2021-09-21) [2] [0]

登录

忘记密码 ?

切换登录

注册