如何使用Spock框架Mock/Spy Jenkins.instance及其链式调用方法
Spock单元测试解决方案
针对Jenkins实例链式调用的Mock场景,直接逐层模拟每个调用节点的返回对象即可,无需启动真实Jenkins服务,完整实现步骤如下:
第一步:封装业务逻辑
建议先把要测试的逻辑封装为独立方法,方便单测调用:
// 业务代码示例 import jenkins.model.Jenkins class DeployVersionHelper { String getLastDeployedVersion(String projectID) { String deployPipeline = "Deploy/${projectID}/deploy-to-prod" def lastRelease = Jenkins.instance.getItemByFullName(deployPipeline).getLastSuccessfulBuild() return lastRelease.getBadgeActions().findResult { String text = it.getText() if (text != null && text ==~ /^Version\=.*/) { return text.find(/\d+\.\d+\.\d+/) } } } }
第二步:编写Spock测试用例
利用Spock的Mock能力逐层模拟链式调用的每个节点,同时处理Jenkins.instance静态属性的Mock:
import spock.lang.Specification import jenkins.model.Jenkins class DeployVersionHelperTest extends Specification { def "测试可以正确从最后一次成功构建的徽章中提取版本号"() { given: "Mock链式调用所有依赖的对象" // 1. Mock Jenkins实例,替换静态instance属性 def mockJenkins = Mock(Jenkins) Jenkins.metaClass.static.getInstance = { mockJenkins } // 2. Mock 部署流水线任务对象 def mockDeployJob = Mock(Object) // 也可以替换为Job对应的真实类名 def expectedPipelinePath = "Deploy/test-project/deploy-to-prod" 1 * mockJenkins.getItemByFullName(expectedPipelinePath) >> mockDeployJob // 3. Mock 最后一次成功构建对象 def mockLastSuccessBuild = Mock(Object) 1 * mockDeployJob.getLastSuccessfulBuild() >> mockLastSuccessBuild // 4. Mock 徽章动作列表,模拟2个徽章,其中一个是版本号徽章 def mockBadge1 = Mock(Object) 1 * mockBadge1.getText() >> "构建人: 张三" def mockBadge2 = Mock(Object) 1 * mockBadge2.getText() >> "Version=1.2.3 20240520" 1 * mockLastSuccessBuild.getBadgeActions() >> [mockBadge1, mockBadge2] when: "调用业务方法获取最后部署版本" def helper = new DeployVersionHelper() def result = helper.getLastDeployedVersion("test-project") then: "验证返回的版本号符合预期" result == "1.2.3" cleanup: "清理元类修改,避免影响其他测试" Jenkins.metaClass = null } def "测试没有匹配的版本号徽章时返回null"() { given: "配置所有徽章都不匹配版本号规则" def mockJenkins = Mock(Jenkins) Jenkins.metaClass.static.getInstance = { mockJenkins } def mockDeployJob = Mock(Object) mockJenkins.getItemByFullName(_) >> mockDeployJob def mockLastSuccessBuild = Mock(Object) mockDeployJob.getLastSuccessfulBuild() >> mockLastSuccessBuild def mockBadge = Mock(Object) mockBadge.getText() >> "普通徽章" mockLastSuccessBuild.getBadgeActions() >> [mockBadge] when: "调用业务方法" def result = new DeployVersionHelper().getLastDeployedVersion("test-project") then: "返回结果为null" result == null cleanup: Jenkins.metaClass = null } }
核心实现说明
- 静态属性
Jenkins.instance通过修改Groovy元类的方式Mock,不需要依赖Jenkins测试框架 - 链式调用的每一层返回值都可以单独定义Mock逻辑,也可以单独校验调用次数、入参是否符合预期
- 如果要做更严格的类型校验,可以把Mock的对象类型替换为Jenkins对应的真实类(比如Item、Run等),不需要引入实现类,只需要依赖Jenkins API依赖即可
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

