You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 10:24:04