使用Jenkins Test Harness + Spock单元测试Jenkins Job DSL脚本时遭遇Jenkins.instance缺失异常
Jenkins Test Harness + Spock单元测试Jenkins Job DSL脚本时遭遇Jenkins.instance缺失异常
看起来你遇到的问题核心是测试代码里的JobManagement没有关联到Jenkins Test Harness启动的测试实例,而是默认去查找全局的Jenkins.instance,但这个全局实例在测试环境中并没有被正确绑定(从你打印的jenkinsRule.instance为null也能看出来,新版本的测试 harness 已经不再用instance字段了)。下面给你几个具体的解决步骤:
1. 修正Jenkins实例的获取方式,关联测试实例到JobManagement
你当前用的JenkinsJobManagement默认会通过全局查找获取Jenkins实例,但测试环境里的实例是由JenkinsRule创建的独立实例,需要手动让JobManagement使用这个实例。修改你的测试类代码:
package com.example import groovy.util.FileNameFinder import javaposse.jobdsl.dsl.DslScriptLoader import javaposse.jobdsl.plugin.JenkinsJobManagement import javaposse.jobdsl.plugin.LookupStrategy import org.junit.ClassRule import org.jvnet.hudson.test.JenkinsRule import spock.lang.Shared import spock.lang.Specification import spock.lang.Unroll class JobRulesSpec extends Specification { @Shared @ClassRule private JenkinsRule jenkinsRule = new JenkinsRule() @Unroll def 'test script #file.name'() { given: // 改用jenkinsRule.jenkins获取测试实例,旧的instance字段已被废弃 def testJenkins = jenkinsRule.jenkins println "==============> ${testJenkins}" // 创建自定义JobManagement,强制使用测试实例 def jobManagement = new JenkinsJobManagement(System.out, [:], new File('.')) { @Override protected LookupStrategy createLookupStrategy() { return new LookupStrategy(LookupStrategy.Strategy.JENKINS_CONTEXT) { @Override protected Object getContext() { return testJenkins } } } } when: new DslScriptLoader(jobManagement).runScript(file.text) then: noExceptionThrown() where: file << new FileNameFinder().getFileNames('src/jobs', '**/*.groovy').collect { new File(it) } } }
这段代码通过覆盖JenkinsJobManagement的createLookupStrategy方法,让它使用测试环境的Jenkins实例,而不是去全局查找。
2. 清理Gradle依赖中的冲突和重复声明
你的build.gradle里有不少重复的依赖声明(比如testImplementation和testCompile重复),还有Spock版本混用的问题(Groovy2.5配Spock2.0会不兼容),建议清理后改成这样:
plugins { id 'groovy' } sourceSets { jobs { groovy { srcDirs 'src/jobs' compileClasspath += main.compileClasspath } compileClasspath += sourceSets.main.output runtimeClasspath += sourceSets.main.output } main { groovy { srcDir 'src/main/groovy' } } test { groovy { srcDir 'src/test/groovy' } } } repositories { mavenCentral() maven { url 'https://repo.jenkins-ci.org/releases/' } } configurations { testPlugins {} testCompile { exclude group: 'xalan' exclude group: 'xerces' } } configurations.all*.exclude group: 'xalan' dependencies { implementation 'org.codehaus.groovy:groovy:2.5.14' implementation ("org.jenkins-ci.plugins:job-dsl-core:${jobDslVersion}") { exclude module: 'xstream' } // Spock版本要匹配Groovy2.5,用1.3-groovy-2.5 testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5' testImplementation 'cglib:cglib-nodep:2.2.2' // Jenkins测试工具依赖 testImplementation('org.jenkins-ci.main:jenkins-test-harness:2146.v6b_f8b_1cb_d12d') { exclude group: 'org.netbeans.modules', module: 'org-netbeans-insane' } testImplementation("org.jenkins-ci.main:jenkins-war:${jenkinsVersion}") { exclude group: 'org.jenkins-ci.ui', module: 'bootstrap' } testImplementation "org.jenkins-ci.main:jenkins-core:${jenkinsVersion}" // Job DSL插件依赖 testImplementation "org.jenkins-ci.plugins:job-dsl:${jobDslVersion}" testImplementation 'org.jenkins-ci.plugins:structs:1.20' testImplementation 'org.jenkins-ci.plugins:script-security:1.54' // 测试环境需要安装的插件 testPlugins 'org.jenkins-ci.plugins:cloudbees-folder:5.14' testPlugins 'org.jenkins-ci.plugins:credentials:2.1.10' testPlugins 'org.jenkins-ci.plugins:cvs:2.13' testPlugins 'org.jenkins-ci.plugins:ghprb:1.40.0' testPlugins 'org.jenkins-ci.plugins:token-macro:2.5' testPlugins 'org.jenkins-ci.plugins.workflow:workflow-cps-global-lib:2.7' } task resolveTestPlugins(type: Copy) { from configurations.testPlugins into new File(sourceSets.test.output.resourcesDir, 'test-dependencies') include '*.hpi' include '*.jpi' def mapping = [:] doFirst { configurations.testPlugins.resolvedConfiguration.resolvedArtifacts.each { mapping[it.file.name] = "${it.name}.${it.extension}" } } rename { mapping[it] } doLast { List<String> baseNames = source*.name.collect { mapping[it] }.collect { it[0..it.lastIndexOf('.') - 1] } new File(destinationDir, 'index').setText(baseNames.join('\n'), 'UTF-8') } } test { dependsOn tasks.resolveTestPlugins inputs.files sourceSets.jobs.groovy.srcDirs useJUnitPlatform() }
3. 检查版本兼容性(关键)
你用的Job DSL 1.77是2020年的老版本,而Jenkins 2.445是2024年的新版本,两者的API差异可能导致很多兼容性问题。建议:
- 要么升级Job DSL到与Jenkins 2.445兼容的版本(比如Job DSL 1.85+)
- 要么降低Jenkins版本到Job DSL 1.77支持的范围(比如Jenkins 2.289.x系列)
如果不想改版本,至少要确保所有依赖的版本相互匹配,避免API不兼容导致的实例初始化失败。
备注:内容来源于stack exchange,提问作者josselin chevalay
相关产品推荐
相关产品推荐

