
👆Spock支持第三种虚拟对象:spies。Spy对象中一部分是真实方法,一部分是虚拟方法。之所以不在第六章介绍是因为Spy具有争议性——意味着代码本身有问题。通常使用在无法重构的遗留代码中。
例子
现有一安全工具——从外部录像机获取视频、检测闯入者并从硬盘从删除证据。应用由Java实现,第一个类负责删除文件,第二个类使用人脸识别算法检测敌人:
public class CameraFeed {//获取录像帧[...code redacted for brevity...]public void setCurrentFrame(Image image){[...code redacted for brevity...]}}public class HardDriveNuker {//从硬盘删除文件public void deleteHardDriveNow(){//立即删除[...code redacted for brevity...]}}public class SmartHardDriveNuker extends HardDriveNuker{//实现人脸识别算法public void activate(CameraFeed cameraFeed){调用deleteHardDriveNow[...code redacted for brevity...]}}
👆上面设计不太好——人脸识别和删除文件在一个类里实现。你可以重构,但是不幸地是,代码被加密。
👆流程
现在不得不硬着头皮编写单元测试。
Spy
显然需要测试的是SmartHardDriveNuker类的activate()方法,activate()中调用deleteHardDriveNow()方法。每一次调用人脸识别方法就删除文件是不现实的,需要在不影响人脸识别算法正常运行的情况下mock删除文件方法。Spock支持Spy——调用真实方法,除非手工指定返回值:
def "automatic deletion of hard disk when agents are here"() {
given: "a camera feed"
CameraFeed cameraFeed = new CameraFeed()
and: "the auto-nuker program"
SmartHardDriveNuker nuker = Spy(SmartHardDriveNuker)//Spy
nuker.deleteHardDriveNow() >> {println "Hard disk is cleared"}//mock不需要实际运行的方法
when:"agents are knocking the door"
cameraFeed.setCurrentFrame(ImageIO.read(getClass().getResourceAsStream( "agents.jpg")))
nuker.activate(cameraFeed);//人脸识别算法运行
then: "all files of hard drive should be deleted"
1 * nuker.deleteHardDriveNow()//验证文件删除被调用
}
👆activate()执行人脸识别算法实际代码,而不执行删除文件的实际代码。
Spy意味着问题

只有在不能重构代码的情况下才使用Spy,一旦使用Spy就意味着代码违反OOP原则。
使用Mock/Stub代替Spy
假设现在可以重构代码,那么应该是这个样子的:
public class SmartHardDriveNuker{//不使用继承
private final HardDriveNuker hardDriveNuker;//通过组合方式重用
public SmartHardDriveNuker(final HardDriveNuker hardDriveNuker) {//构造方法注入
this.hardDriveNuker = hardDriveNuker;
}
public void activate(CameraFeed cameraFeed) {
[...code redacted for brevity..]
hardDriveNuker.deleteHardDriveNow();//调用外部依赖方法
[...code redacted for brevity..]
}
}
def "automatic deletion of hard disk when agents are here"() {
given: "a camera feed and a fake nuker"
CameraFeed cameraFeed = new CameraFeed()
HardDriveNuker nuker = Mock(HardDriveNuker)//Mock代替Spy
and: "the auto-nuker program"
SmartHardDriveNuker smartNuker = new SmartHardDriveNuker(nuker)//诸如
when:"agents are knocking the door"
cameraFeed.setCurrentFrame(ImageIO.read(getClass().getResourceAsStream("agents.jpg")))
smartNuker.activate(cameraFeed);//调用算法
then: "all files of hard drive should be deleted"
1 * nuker.deleteHardDriveNow()//验证
}
👆可以看到使用Mock替代了Spy,源码也更符合OOP原则。
