简体   繁体   中英

Jenkins-spock : method stub fails depending on the platform

Here is a (simplified) piece of code and the related unit test I wrote, and for which I was happy until recently.

File vars/deleteFile.groovy

@NonCPS
def call(String path) {
    new File(path).delete() //Must be declared as authorized class & method in Jenkins
}

File vars/toto.groovy:

def call(Map params = [:] ) {
...
    deleteFile(params.get('envFile'))
...
}

File tests/vars/TotoSpec.groovy:

class TotoSpec extends JenkinsPipelineSpecification {

    def toto = null

    def setup() {
        toto = loadPipelineScriptForTest('vars/toto.groovy')
        ...
    }

    def '[toto] test env file'() {
        when:
        toto envFile: 'env.list'

        then:
        ...
        1 * getPipelineMock('deleteFile.call').call('env.list')
    }

}

The test used to pass both on my local machine and the CI slave. However, I don't know why, this started to fail on my local machine (but not on the slave):

java.lang.IllegalStateException: 
There is no pipeline step mock for [deleteFile.call].
        1. Is the name correct?
        2. Does the pipeline step have a descriptor with that name?
        3. Does that step come from a plugin? If so, is that plugin listed as a dependency in your pom.xml?
        4. If not, you may need to call explicitlyMockPipelineStep('deleteFile.call') in your test's setup: block.
        at TotoSpec.[toto] test env file(TotoSpec.groovy:90)

I therefore performed the following update on my unit test, though I'm not sure if deleteFile is really a pipeline step since it has no body/closure.

File tests/vars/TotoSpec.groovy:

class TotoSpec extends JenkinsPipelineSpecification {

    def setup() {
        toto = loadPipelineScriptForTest('vars/toto.groovy')
        explicitlyMockPipelineStep('deleteFile')
        ...
    }

    def '[toto] test env file'() {
        when:
        toto envFile: 'env.list'

        then:
        ...
        1 * getPipelineMock('deleteFile').call('env.list')
    }

}

Now the unit test is OK on my local machine, but fails on the CI slave:

org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Mock Generator for [deleteFile]' with class 'com.homeaway.devtools.jenkins.testing.PipelineVariableImpersonator' to class 'groovy.lang.Closure'
    at TotoSpec.setup(TotoSpec.groovy:32)

What am I missing ?

Local configuration:

$ java --version
openjdk 14.0.2 2020-07-14
OpenJDK Runtime Environment (build 14.0.2+12-Ubuntu-120.04)
OpenJDK 64-Bit Server VM (build 14.0.2+12-Ubuntu-120.04, mixed mode, sharing)

mvn --version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 14.0.2, vendor: Private Build, runtime: /usr/lib/jvm/java-14-openjdk-amd64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "4.19.128-microsoft-standard", arch: "amd64", family: "unix"

CI Slave configuration:

$ java --version
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment (build 11.0.6+10-post-Debian-1bpo91)
OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Debian-1bpo91, mixed mode)
$ /home/jenkins/tools/hudson.tasks.Maven_MavenInstallation/Maven_3.6.3/apache-maven-3.6.3/bin/mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /home/jenkins/tools/hudson.tasks.Maven_MavenInstallation/Maven_3.6.3/apache-maven-3.6.3
Java version: 11.0.6, vendor: Debian, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: fr_FR, platform encoding: UTF-8
OS name: "linux", version: "4.9.0-14-amd64", arch: "amd64", family: "unix"

Dependencies in pom.xml

 <properties>
        <groovy.core.version>2.4.17</groovy.core.version>
        <groovy.gmaven.pluginVersion>1.6.1</groovy.gmaven.pluginVersion>
        <google.guava.version>20.0</google.guava.version>

        <jenkins-spock.version>2.0.0</jenkins-spock.version>
        <jenkins.version>2.102</jenkins.version>
        <jenkins.servlet.version>3.1.0</jenkins.servlet.version>
        <jenkins.workflow.cps.version>2.36</jenkins.workflow.cps.version>
        <jenkins.workflow.basic.steps.version>2.6</jenkins.workflow.basic.steps.version>
        <jenkins.workflow.durable.task.step.version>2.21</jenkins.workflow.durable.task.step.version>
        <jenkins.workflow.stage.step.version>2.3</jenkins.workflow.stage.step.version>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <junit.version>4.12</junit.version>
        <junit.plugin.version>1.24</junit.plugin.version>
        <surefire.pluginVersion>2.22.0</surefire.pluginVersion>

        <logback.configration>logback-test.xml</logback.configration>
        <logdir>${project.build.directory}/log</logdir>
        <test.loglevel>ERROR</test.loglevel>
        <log.logback.version>1.2.3</log.logback.version>
        <log.slf4j.version>1.7.25</log.slf4j.version>
    </properties>
...
<dependencies>
        <dependency>
            <groupId>com.homeaway.devtools.jenkins</groupId>
            <artifactId>jenkins-spock</artifactId>
            <version>${jenkins-spock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${log.logback.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${log.logback.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jenkins-ci.main</groupId>
            <artifactId>jenkins-core</artifactId>
            <version>${jenkins.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jenkins-ci.plugins.workflow</groupId>
            <artifactId>workflow-basic-steps</artifactId>
            <version>${jenkins.workflow.basic.steps.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jenkins-ci.plugins.workflow</groupId>
            <artifactId>workflow-cps</artifactId>
            <version>${jenkins.workflow.cps.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <!-- provides sh() step -->
            <groupId>org.jenkins-ci.plugins.workflow</groupId>
            <artifactId>workflow-durable-task-step</artifactId>
            <version>${jenkins.workflow.durable.task.step.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <!-- provides stage() step -->
            <groupId>org.jenkins-ci.plugins</groupId>
            <artifactId>pipeline-stage-step</artifactId>
            <version>${jenkins.workflow.stage.step.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${jenkins.servlet.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jenkins-ci.plugins</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.plugin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>${groovy.core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${log.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${log.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

Having carefully read the documentation again, since deleteFile is declared by myself in the vars directory, it is considered as a Pipeline Shared Library Global Variable.

Therefore, switching to the following unit test now works correctly everywhere:

class TotoSpec extends JenkinsPipelineSpecification {

    def setup() {
        toto = loadPipelineScriptForTest('vars/toto.groovy')
        explicitlyMockPipelineVariable('deleteFile')
        ...
    }

    def '[toto] test env file'() {
        when:
        toto envFile: 'env.list'

        then:
        ...
        1 * getPipelineMock('deleteFile.call').call('env.list')
    }
}

It remains unclear why I suddenly needed to use explicitlyMockPipelineVariable('deleteFile') on my machine, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM