使用 spock 模拟 groovy 脚本
Mock groovy script using spock
我有以下脚本文件
aScript.groovy 和 bScript.groovy 都在 vars 文件夹中,这些脚本将从 jenkinsFile 中调用 在项目文件夹中
aScript.groovy
def call(){
return bScript()
}
bScript.groovy
def call(){
return "bar"
}
我正在使用 gradle 构建我的 build.gradle 文件,如下所示
apply plugin: 'groovy'
apply plugin: 'project-report'
repositories {
mavenCentral()
}
sourceSets {
main {
groovy {
srcDirs = ['src/main/groovy']
}
output.resourcesDir = "build/resources/test/lib/commons@master/vars"
}
test {
groovy {
srcDirs = ['src/test/groovy']
}
output.resourcesDir = "build/resources/test"
}
}
sourceSets.main.resources { srcDirs = ['vars', 'src/main/resources', 'resources'] }
sourceSets.test.resources { srcDirs = ['src/test/resources'] }
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.9'
testCompile 'junit:junit:4.12'
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
testCompile 'com.lesfurets:jenkins-pipeline-unit:1.0'
testCompile 'cglib:cglib-nodep:3.2.2'
testCompile 'org.objenesis:objenesis:1.2'
testCompile 'org.assertj:assertj-core:3.4.1'
compile ('org.jenkins-ci.main:jenkins-core:2.46.3'){
exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
}
compile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'org.slf4j:slf4j-log4j12:1.7.21'
compile 'commons-beanutils:commons-beanutils:1.9.3'
compile group: 'org.apache.ivy', name:'ivy', version:'2.2.0'
}
测试类
class aScriptSpec extends Specification {
/**
* Delegate to the test helper
*/
@Delegate JenkinsPipelineTestHelper pipelineTestHelper
def setup() {
pipelineTestHelper = new JenkinsPipelineTestHelper()
pipelineTestHelper.setUp()
pipelineTestHelper.setJobVariables()
def library = library().name('commons')
.defaultVersion("master")
.allowOverride(true)
.implicit(false)
.targetPath(sharedLibs)
.retriever(localSource(sharedLibs))
.build()
helper.registerSharedLibrary(library)
helper.registerAllowedMethod('timestamps', [Closure.class], null)
helper.registerAllowedMethod('node', [Closure.class], null)
helper.registerAllowedMethod("stage", [String.class], null)
}
def "pipeline test"() {
given:
addEnvVar('workerLabel','testworker')
addEnvVar('BRANCH_NAME','PRbuild')
//to mock bScript
helper.registerAllowedMethod("bScript", [], null)//1. tried using registerAllowedMethod
bScript.metaClass.static.call = {return 'bar'} //2. tried using metaClass
binding.setVariable('bScript', new Object() { //3. tried using metaClass
def call() {
println "bScript call mocked"
}
})
when:
loadScript('Jenkinsfile')
then:
printCallStack()
}
}
class JenkinsPipelineTestHelper extends BasePipelineTest{
@Rule
public TemporaryFolder folder = new TemporaryFolder()
String sharedLibs = this.class.getResource('/lib').getFile()
void setUp() throws Exception {
scriptRoots += 'vars'
super.setUp()
}
/**
* Variables that Jenkins expects
*/
void setJobVariables() {...}
void addEnvVar(String name, String val) {...}
}
我的项目结构
app
\--build
\--classes
\--test
\--aScriptSpec.class
\--resources
\--test
\--lib
\--commons@master
\--vars
\-- aScript.groovy
|--bScript.groovy
|--src/test/groovy
\--aScriptSpec.groovy
|--vars
\--aScript.groovy
|--bScript.groovy
|--build.gradle
\--Jenkinsfile
我需要测试 aScript 功能,所以我想模拟 bscript() 我尝试使用 bScript.metaClass.static.call = {return 'bar'}
它给了我groovy.lang.MissingPropertyException: No such property: bScript for class: aScriptSpec
如何模拟 bScript()?
我认为 aScript.groovy 和 bScript.groovy 应该在 var[s] 中而不是在 var 文件夹中。
.
└── vars
├── aScript.groovy
└── bScript.groovy
如果你想模拟 bScript,那么下面的代码就像一个魅力
helper.registerAllowedMethod("bScript", [], { return 'foo'})
更新:
我刚刚复制了那个。
我认为这不是你第一次模拟这样的方法:
helper.registerAllowedMethod("bScript", [], {'foo'})
或 groovyClassPath 中出现的任何此特定对象
当你打电话给
def script = loadScript("template/pipeline/template.groovy")
此时加载您的管道 classPath 再次更新为您的 lib 中定义的对象。所以我想你的模拟被覆盖了。
所以这里的解决方法是将管道主体包装在一个方法中,比如 someMethod :
@Library("commons")
import java.io.File // or whatever
echo aScript() // this is not mocked
def someMethod() {
echo aScript() // this one could be mocked
}
return this
那么你的测试可能是这样的:
@Test
void mock_bscript() throws Exception {
helper.registerAllowedMethod("bScript", [], {'foo'}) // this one doesn't work
def script = loadScript("template/pipeline/template.groovy")
helper.registerAllowedMethod("bScript", [], {'ololo'}) // this does
script.someMethod()
printCallStack()
}
所以调用堆栈将是
Loading shared library jenkins-commons with version master
template.run()
template.aScript()
aScript.bScript()
template.echo(bar)
template.someMethod()
template.aScript()
aScript.bScript()
template.echo(ololo)
我有以下脚本文件 aScript.groovy 和 bScript.groovy 都在 vars 文件夹中,这些脚本将从 jenkinsFile 中调用 在项目文件夹中
aScript.groovy
def call(){
return bScript()
}
bScript.groovy
def call(){
return "bar"
}
我正在使用 gradle 构建我的 build.gradle 文件,如下所示
apply plugin: 'groovy'
apply plugin: 'project-report'
repositories {
mavenCentral()
}
sourceSets {
main {
groovy {
srcDirs = ['src/main/groovy']
}
output.resourcesDir = "build/resources/test/lib/commons@master/vars"
}
test {
groovy {
srcDirs = ['src/test/groovy']
}
output.resourcesDir = "build/resources/test"
}
}
sourceSets.main.resources { srcDirs = ['vars', 'src/main/resources', 'resources'] }
sourceSets.test.resources { srcDirs = ['src/test/resources'] }
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.9'
testCompile 'junit:junit:4.12'
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
testCompile 'com.lesfurets:jenkins-pipeline-unit:1.0'
testCompile 'cglib:cglib-nodep:3.2.2'
testCompile 'org.objenesis:objenesis:1.2'
testCompile 'org.assertj:assertj-core:3.4.1'
compile ('org.jenkins-ci.main:jenkins-core:2.46.3'){
exclude group: 'org.slf4j', module: 'log4j-over-slf4j'
}
compile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'org.slf4j:slf4j-log4j12:1.7.21'
compile 'commons-beanutils:commons-beanutils:1.9.3'
compile group: 'org.apache.ivy', name:'ivy', version:'2.2.0'
}
测试类
class aScriptSpec extends Specification {
/**
* Delegate to the test helper
*/
@Delegate JenkinsPipelineTestHelper pipelineTestHelper
def setup() {
pipelineTestHelper = new JenkinsPipelineTestHelper()
pipelineTestHelper.setUp()
pipelineTestHelper.setJobVariables()
def library = library().name('commons')
.defaultVersion("master")
.allowOverride(true)
.implicit(false)
.targetPath(sharedLibs)
.retriever(localSource(sharedLibs))
.build()
helper.registerSharedLibrary(library)
helper.registerAllowedMethod('timestamps', [Closure.class], null)
helper.registerAllowedMethod('node', [Closure.class], null)
helper.registerAllowedMethod("stage", [String.class], null)
}
def "pipeline test"() {
given:
addEnvVar('workerLabel','testworker')
addEnvVar('BRANCH_NAME','PRbuild')
//to mock bScript
helper.registerAllowedMethod("bScript", [], null)//1. tried using registerAllowedMethod
bScript.metaClass.static.call = {return 'bar'} //2. tried using metaClass
binding.setVariable('bScript', new Object() { //3. tried using metaClass
def call() {
println "bScript call mocked"
}
})
when:
loadScript('Jenkinsfile')
then:
printCallStack()
}
}
class JenkinsPipelineTestHelper extends BasePipelineTest{
@Rule
public TemporaryFolder folder = new TemporaryFolder()
String sharedLibs = this.class.getResource('/lib').getFile()
void setUp() throws Exception {
scriptRoots += 'vars'
super.setUp()
}
/**
* Variables that Jenkins expects
*/
void setJobVariables() {...}
void addEnvVar(String name, String val) {...}
}
我的项目结构
app
\--build
\--classes
\--test
\--aScriptSpec.class
\--resources
\--test
\--lib
\--commons@master
\--vars
\-- aScript.groovy
|--bScript.groovy
|--src/test/groovy
\--aScriptSpec.groovy
|--vars
\--aScript.groovy
|--bScript.groovy
|--build.gradle
\--Jenkinsfile
我需要测试 aScript 功能,所以我想模拟 bscript() 我尝试使用 bScript.metaClass.static.call = {return 'bar'}
它给了我groovy.lang.MissingPropertyException: No such property: bScript for class: aScriptSpec
如何模拟 bScript()?
我认为 aScript.groovy 和 bScript.groovy 应该在 var[s] 中而不是在 var 文件夹中。
.
└── vars
├── aScript.groovy
└── bScript.groovy
如果你想模拟 bScript,那么下面的代码就像一个魅力
helper.registerAllowedMethod("bScript", [], { return 'foo'})
更新:
我刚刚复制了那个。
我认为这不是你第一次模拟这样的方法:
helper.registerAllowedMethod("bScript", [], {'foo'})
或 groovyClassPath 中出现的任何此特定对象
当你打电话给
def script = loadScript("template/pipeline/template.groovy")
此时加载您的管道 classPath 再次更新为您的 lib 中定义的对象。所以我想你的模拟被覆盖了。
所以这里的解决方法是将管道主体包装在一个方法中,比如 someMethod :
@Library("commons")
import java.io.File // or whatever
echo aScript() // this is not mocked
def someMethod() {
echo aScript() // this one could be mocked
}
return this
那么你的测试可能是这样的:
@Test
void mock_bscript() throws Exception {
helper.registerAllowedMethod("bScript", [], {'foo'}) // this one doesn't work
def script = loadScript("template/pipeline/template.groovy")
helper.registerAllowedMethod("bScript", [], {'ololo'}) // this does
script.someMethod()
printCallStack()
}
所以调用堆栈将是
Loading shared library jenkins-commons with version master
template.run()
template.aScript()
aScript.bScript()
template.echo(bar)
template.someMethod()
template.aScript()
aScript.bScript()
template.echo(ololo)