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
大佬,能写一个 如何 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
)