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

描述规范下的对象与其合作者之间(强制)交互的行为。
说人话就是Mock()的对象是一个虚拟类,用于替换真实的类,为每个方法调用返回一个默认值:引用类型是null,基本类型为 0 或 false,比如可以把调用其他服务的接口、数据库访问、Redis等资源的访问给模拟掉,返回我们事先准备好的假数据。
目前文章里大部分代码示例使用的都是Mock()这个方法,模拟的优点是可以将被测类与系统的其余部分隔离掉,不会进行真实的调用。
Spock的Mock方法不仅可以模拟方法返回结果,还可以模拟方法行为 ,比如之前的文章中介绍的验证某个方法的调用次数:

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

Stub

存根是使协作者以某种方式响应方法调用的行为。当存根方法时,你不关心该方法是否以及将被调用多少次;你只是希望它在被调用时返回一些值
Stub()存根方法也是一个虚拟类,比Mock()方法更简单一些,只返回事先准备好的假数据,而不提供交互验证(即该方法是否被调用以及将被调用多少次)。使用存根Stub只能验证状态(例如测试方法返回的结果数据是否正确,list大小等,是否符合断言)。
所以Mock比Stub的功能更多一些,但如果我们只是验证结果使用Stub就足够了,用法和Mock一样,而且更轻量一些。
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
在这个努力程度如此低下的时代,还轮不到比拼天赋。静下心来,just do it

大佬,能写一个 如何 mock ClassA中 一个方法call() 中使用 resttemplate 的例子吗?
@spock小弟你是要mock掉http的rest模板请求吗?我理解就是正常的mock就行了,如果有什么问题可以提供下代码,我帮你看下
你的解答对我很有帮助,多谢!
你好,请教一下,可以使用Spock模拟Http请求吗?类似于Spring的MockMvc
@暗夜有魅这个感觉跟用什么单测框架无关,目前我的文章都是基于单元测试,即当前类中的某个方法测试,所以不关心是不是http的请求,比如一般项目中都会封装个
HttpUtils的工具类,如果你的代码中有调用HttpUtils的方法,直接mock掉就行了。spy案例中被测方法里调用多个外部方法,这种场景似乎很常见。
请教一下,为什么会有坏味道呢?
或许这样会导致跨层测试?
@guodage迟复为歉!如文中所述Spy的例子:
methodA内部调用了methodB,但这两个方法属于同一个类MyClass,所以不存在跨层调用(当然如果是跨层调用的话建议使用mock或stub避免,否则就超出单元测试的范畴了,这一点你说的对),我举的Spy场景是说,同一个类中,我要测试methodA方法,但methodA内部调用了methodB,而我又不想测试methodB,这种场景。这种场景为什么有坏味道呢?因为我觉得正常情况你测试了
methodA方法,那么methodA方法内部调用的当前类的其他方法(比如methodB)都应该被测试到,这样才能保证单测的覆盖率,除非说之前已经有一个方法比如methodC内部已经测试过methodB了(但这种情况下其实也没必要spy掉methodB)