Giter Club home page Giter Club logo

jenkins-spock's Introduction

Jenkins Pipeline Support for Spock

Utility classes to help with testing Jenkins pipeline scripts and functions written in Groovy.

User Guide (GroovyDoc)

Add this library to pom.xml in the test scope:

<dependency>
	<groupId>com.homeaway.devtools.jenkins</groupId>
	<artifactId>jenkins-spock</artifactId>
	<scope>test</scope>
</dependency>

Check the CHANGELOG.md to find details about the available versions.

Working Examples

The examples directory contains working sample projects that show off the major kinds of project this library can be used with. Check them out and try building them yourself!

Specifications

This library provides a JenkinsPipelineSpecification class that extends the Spock testing framework's Specification class. To test Jenkins pipeline Groovy code, extend JenkinsPipelineSpecification instead of Specification. Please see the GroovyDoc for JenkinsPipelineSpecification for specific usage information and the Spock Framework Documentation for general usage information.

During the tests of a JenkinsPipelineSpecification suite,

  1. All Jenkins pipeline steps (@StepDescriptors) will be globally callable, e.g. you can just write sh( "echo hello" ) anywhere.
    1. "Body" closure blocks passed to any mock pipeline steps will be executed.
  2. All Jenkins pipeline variables (@Symbols and GlobalVariables) will be globally accessible, e.g. you can just write docker.inside(...) anywhere
  3. All Pipeline Shared Library Global Variables (from the /vars directory) will be globally accessible, so you can just use them anywhere.
  4. All interactions with any of those pipeline extension points will be captured by Spock mock objects.
  5. You can load any Groovy script (Jenkinsfile or Shared Library variable) to unit-test it in isolation.
  6. The Jenkins singleton instance will exist as a Spock mock object.
  7. The CpsScript execution will exist as a Spock spy object (you should never need to interact with this, but it's there).

Dependencies

There are some dependencies of this library that are marked with Maven's provided scope. This means that Maven will pull them in for building and testing this library, but when you use this library you must pull those libraries in as dependencies yourself.

This is done because these dependencies - things like the Jenkins Pipeline API, JUnit, etc - are things that

  1. You absolutely have to have as dependencies in your project in order for this library to be of any use
  2. Should not have their version or final scope controlled by this library

The dependencies that should already be in your project in order for using this library to make any sense are:

<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-step-api</artifactId>
	<version>${jenkins.workflow.step.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>
	<groupId>org.jenkins-ci</groupId>
	<artifactId>symbol-annotation</artifactId>
	<version>${jenkins.symbol.version}</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>${junit.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>

Depending on your parent pom, some of the ${jenkins.version} properties may already be defined. Be sure you define any that are not.

If your code actually writes code against classes in any of these dependencies, remove the <scope>test</scope> entry for the corresponding block(s).

Developer Guide

Building

The build of jenkins-spock is built with Maven. Normal Maven lifecycle phases apply. As long as you have a contemporary (1.8+) JDK and Maven (3.3+), it should build fine.

Testing

Unit tests of jenkins-spock will happen automatically during the test phase of the Maven build.

There is an it Maven Profile that can be activated to run integration tests:

mvn verify -Pit

The integration tests will run mvn verify on some of the Working Example Projects, using the current jenkins-spock code.

Releasing

jenkins-spock should be released by the maven-release-plugin:

mvn clean release:prepare release:perform

In order for this to succeed, the user running this must

  1. Configure GitHub credentials with push access to this repository.
  2. Configure Sonatype Nexus credentials with deploy access to the com.homeaway groupId.
  3. Configure a PGP identity so that the maven-gpg-plugin can sign artifacts.
    1. Locally, run GPG 2.1 or newer
    2. Set the default signing key to the key you want to use
    3. Provide -Dgpg.passphrase on the command-line or run interactively to be able to enter a passphrase

jenkins-spock's People

Contributors

awittha avatar bszeman9 avatar dariuszkuc avatar neoword avatar ningzhuredsilence avatar obfischer avatar rnc avatar wheelerlaw avatar xaseron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jenkins-spock's Issues

@NonCPS annotation inside Jenkinsfile

Hi,
This is more of a question than an issue, and I couldn't find a better place to ask.
In some of our Jenkinsfiles we use functions outside of node() block, I know it's not a best practice, but anyway it looks like this:

Jenkinsfile

node('ci'){...)
@NonCPS
String someRegexFn(String){ ... )

is there any way to test this function in jenkins-spock without having to add:

import com.cloudbees.groovy.cps.NonCPS;

To the Jenkinsfile?

I guess it is a similar issue to #21 (comment)

Loading libraries dynamically

Desired Behavior

It should be capable to fetch and load Shared libraries dynamically when the Jenkinsfile have the following syntax

def lib = library('my-shared-library').com.mycorp.pipeline 

and return the package with all classes.
At the moment after I add the dependency

        <dependency>
            <groupId>org.jenkins-ci.plugins.workflow</groupId>
            <artifactId>workflow-cps-global-lib</artifactId>
            <version>2.12</version>
            <scope>test</scope>
        </dependency>

I recived the follow error from maven


java.lang.Exception: controller: Pipeline failed: java.lang.NullPointerException: Cannot get property 'com' on null object

Benefits

Please list the benefits of updating the project to have the new behavior, e.g.

  1. Will allow to test the pipelines using PSL that are not in the local development

Integrate with a CI service

There should be a CI service integrated that can

  1. show build health on the main README
  2. validate code changes in Pull Requests

TravisCI is a promising candidate.

NullPointerException

New user so please help to set it up right. After adding the dependency to pom file I extended a working spec file from JenkinsPipelineSpecification. I'm getting the following exception,

java.lang.NullPointerException
at hudson.model.Descriptor.getConfigFile(Descriptor.java:904)
at hudson.model.Descriptor.load(Descriptor.java:892)
at com.xebialabs.xlrelease.ci.XLReleaseNotifier$XLReleaseDescriptor.load(XLReleaseNotifier.java:225)
at com.xebialabs.xlrelease.ci.XLReleaseNotifier$XLReleaseDescriptor.(XLReleaseNotifier.java:188)
at com.xebialabs.xlrelease.ci.workflow.XLReleaseStep$XLReleaseStepDescriptor.(XLReleaseStep.java:88)
at java.lang.Class.newInstance(Class.java:442)
at com.homeaway.devtools.jenkins.testing.APipelineExtensionDetector.getPipelineSteps(APipelineExtensionDetector.java:83)
at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.setupSpec(JenkinsPipelineSpecification.groovy:878)

Any help would be appreciated. My POM file,


4.0.0

<groupId>com.COMPANY</groupId>
<artifactId>TEST_PROJ</artifactId>
<version>0.0.1</version>

<build>
    <sourceDirectory>src</sourceDirectory>
    <testSourceDirectory>tests</testSourceDirectory>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>3.0.0</version>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>add-source</goal>
                    </goals>
                    <configuration>
                        <sources>
                            <source>vars</source>
                        </sources>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>1.7.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <!--<goal>testCompile</goal>-->
                        <goal>execute</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <sources>
                    <source>
                        <directory>src</directory>
                        <includes>
                            <include>**/*.groovy</include>
                        </includes>
                    </source>
                    <source>
                        <directory>vars</directory>
                        <includes>
                            <include>**/*.groovy</include>
                        </includes>
                    </source>
                </sources>
                <testSources>
                    <testSource>
                        <directory>test</directory>
                        <includes>
                            <include>**/*.groovy</include>
                        </includes>
                    </testSource>
                </testSources>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.4</version>
            <executions>
                <execution>
                    <id>default-prepare-agent</id>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-report</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-check</id>
                    <goals>
                        <goal>check</goal>
                    </goals>
                    <configuration>
                        <rules>
                            <rule implementation="org.jacoco.maven.RuleConfiguration">
                                <element>BUNDLE</element>
                                <limits>
                                    <limit implementation="org.jacoco.report.check.Limit">
                                        <counter>COMPLEXITY</counter>
                                        <value>COVEREDRATIO</value>
                                        <minimum>0.0</minimum>
                                    </limit>
                                </limits>
                            </rule>
                        </rules>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
            <configuration>
                <skipTests>false</skipTests>
                <includes>
                    <include>**/*Spec.class</include>
                </includes>
                <systemPropertyVariables>
                    <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
                </systemPropertyVariables>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>com.cloudbees</groupId>
        <artifactId>groovy-cps</artifactId>
        <version>1.27</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.9</version>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-library</artifactId>
        <version>2.1</version>
    </dependency>
    <dependency>
        <groupId>org.spockframework</groupId>
        <artifactId>spock-core</artifactId>
        <version>1.3-groovy-2.5</version>
        <exclusions>
            <exclusion>
                <artifactId>groovy-all</artifactId>
                <groupId>org.codehaus.groovy</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>3.2.9</version>
    </dependency>
    <dependency>
        <groupId>org.objenesis</groupId>
        <artifactId>objenesis</artifactId>
        <version>3.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
    <dependency>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.4</version>
    </dependency>
    <dependency>
        <groupId>org.jacoco</groupId>
        <artifactId>org.jacoco.core</artifactId>
        <version>0.8.4</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy</artifactId>
        <version>2.5.4</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci.main</groupId>
        <artifactId>jenkins-core</artifactId>
        <version>2.177</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci.plugins</groupId>
        <artifactId>jacoco</artifactId>
        <version>2.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci.plugins</groupId>
        <artifactId>jacoco</artifactId>
        <version>2.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci.plugins.workflow</groupId>
        <artifactId>workflow-step-api</artifactId>
        <version>2.13</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.hudson</groupId>
        <artifactId>hudson-core</artifactId>
        <version>3.3.3</version>
    </dependency>
    <dependency>
        <groupId>com.xebialabs.ci</groupId>
        <artifactId>xlrelease-plugin</artifactId>
        <version>7.5.4</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci.plugins.workflow</groupId>
        <artifactId>workflow-cps</artifactId>
        <version>2.36</version>
    </dependency>
    <dependency>
        <groupId>org.jenkins-ci</groupId>
        <artifactId>symbol-annotation</artifactId>
        <version>1.10</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.homeaway.devtools.jenkins</groupId>
        <artifactId>jenkins-spock</artifactId>
        <version>2.0.1</version>
    </dependency>
</dependencies>

jenkins-spock incorrectly wraps consumed pipeline libraries and conceals errors

Expected Behavior

When calling a pipeline specification that in turn consumes helper objects for additional functionality jenkins-spock tries to be helpful on methods that are not found by trying to attempt to load pipeline method specifications and then displays unhelpful information above the actual stack trace. An example is given in the actual behavior information below.

At minimum it would be great if the original stack information is provided before the 'unknown pipelien step' information or otherwise highlighted.

Actual Behavior

// var/myStepFunction.groovy
call(Map<String, String> params) {
  myHelper = new MyHelper()
  myHelper.thing()
}

// MyHelper.groovy
class MyHelper {
   def thing() {
     /// does things independently of the pipeline
     otherClass = otherClass.new()
     otherClass.methodCall(incorrectParameter)
  } 
}

// otherClass.groovy
class otherClass {
  // note the lack of parameters  
  def methodCall() {
    // does things
  }
}

With the above code (or something similar) when you call loadPipelineSpecification('vars/myStepFunction.groovy') and then invoke the call function it will see that methodCall is incorrect and try to wrap it into a pipeline step. If that is not available it will fail and report that there is a pipeline step called but there is no mock for it. That isn't the right error however since a) this instance method is not using a pipeline step at all and b) the original error would have helpful debugging information that is not displayed immediately.

The original error is available in the stack trace, but providing the additional information above is confusing to end users.

Steps to Reproduce

See the above class definition and provided information

Declarative pipeline help

I have a declarative pipeline that i am trying to write tests for..

vars/examplePipeline.groovy

def call(settings = [:]) {
  pipeline {
    agent none
    stages {
      stage('Example') {
        agent any
        steps {
            ...
        }
      }
    }
  }
}

ExamplePipelineSpec.groovy

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

public class ExamplePipelineSpec extends JenkinsPipelineSpecification {
  def examplePipeline = null

  def setup() {
    examplePipeline = loadPipelineScriptForTest("vars/examplePipeline.groovy")
  }

  def "[examplePipeline] will load" () {
    when:
      examplePipeline()
    then:
      assert true
  }
}

I have added the pipeline-model-definition plugin as a dependency, which should be all i need. However when i run the test, i get this error During a test, the pipeline step [agent] was called but there was no mock for it.. @awittha would you have any idea why jenkins-spock would not be mocking out that step, even though it is in the plugin?

Mocking "env" var without a Jenkinsfile

I'm trying to mock the "env" var for a test which doesn't otherwise require a Jenkinsfile. Most of the examples and documentation I see uses getBinding().setVariable() on the Jenkinsfile, as discussed in this issue.

I did see this alternate method but when I use that, it still errors with groovy.lang.MissingPropertyException: No such property: env for class: myClass

I've tried including the explicitlyMockPipelineVariable("env") line in the setup() function as shown in the example, as well as in a setup: clause within the test itself. Both attempts result in the same error.

Compilation Error: Source option 5 is no longer supported. Use 6 or later.

Expected Behavior

The shared-library example tests run

Actual Behavior

An error is received:

[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] Source option 5 is no longer supported. Use 6 or later.
[ERROR] Target option 1.5 is no longer supported. Use 1.6 or later.
[INFO] 2 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  06:01 min
[INFO] Finished at: 2019-04-20T10:47:35-04:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project jenkinsfile-test-shared-library: Compilation failure: Compilation failure:
[ERROR] Source option 5 is no longer supported. Use 6 or later.
[ERROR] Target option 1.5 is no longer supported. Use 1.6 or later.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

Steps to Reproduce

  1. Clone the Repository
  2. Navigate to <repo_root>/examples/shared-library
  3. Run mvn clean verify

Additional Information

Output of mvn --version:

Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-04T15:00:29-04:00)
Maven home: C:\Users\brian\scoop\apps\maven\current\bin\..
Java version: 11.0.2, vendor: Oracle Corporation, runtime: C:\Users\brian\scoop\apps\openjdk\current
Default locale: en_US, platform encoding: Cp1252
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Mock (implicit-*) steps

Greetings!

I want to test a shared library function in /vars. I mocked the step in a then-block:

then: 1 * getPipelineMock("prepare.call")(_)

However, the invoked step is declared as implicit-expected and the mock in the then-block ignores the invoked step.

The code is from the test report:

1 * getPipelineMock("prepare.call")(_)   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * (implicit-expected) getPipelineMock("prepare.call").call()

The same issue also occured when I tried to test non-shared-library steps like jobDsl.

Explicitly mocking the step with explicitlyMockPipelineStep("prepare.call") did not help either.

What can I do to let the invocations match?

Kind Regards,
Oliver

Unable to load global PSL

Expected Behavior

Loads the file in the vars/ directory properly without removing a slash.
Actual Behavior

Cannot open URL: file:/path/to/target/test-classes/varsstep.groovy

Steps to Reproduce

The failing code is defined as:
def step = loadPipelineScriptForTest("vars/step.groovy")

And my tree is defined as:

โ”œโ”€โ”€ src
โ”‚ย ย  โ”œโ”€โ”€ dir
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ dir
โ”‚ย ย  โ”‚ย ย      โ””โ”€โ”€ dir
โ”‚ย ย  โ”‚ย ย          โ”œโ”€โ”€ StepClass.groovy
โ”‚ย ย  โ””โ”€โ”€ pipeline.gdsl
โ”œโ”€โ”€ test
โ”‚ย ย  โ””โ”€โ”€ unit
โ”‚ย ย      โ””โ”€โ”€ groovy
โ”‚ย ย          โ””โ”€โ”€ net
โ”‚ย ย              โ””โ”€โ”€ inseng
โ”‚ย ย                  โ””โ”€โ”€ jenkins
โ”‚ย ย                      โ””โ”€โ”€ stepTest.groovy
โ””โ”€โ”€ vars
    โ”œโ”€โ”€ step.groovy

This shouldn't fail, and it shouldn't strip my path.

How to mock static method of some JDK class?

I need to generate some UUID values during pipeline run.
To check even this behaviour I need an advice howto mock the following statement:

UUID.randomUUID().toString()

I tried the following in the setup block but without success:

final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f"
def uuid = UUID.fromString(id)
explicitlyMockPipelineStep('UUID.randomUUID')() >> uuid

Library is trying to create classes from resource files

Expected Behavior

The library should ignore resource files

Actual Behavior

The library is scanning all folders and tries to create classes from resource files.

Steps to Reproduce

  1. Add a text file to the main/resources folder
  2. Run a Test
  3. The scanner will read the text file and will try to create a class from the text file throwing several exceptions in the process

Classloader issues

Expected Behavior / Actual Behavior

I am seeing some strange behaviour with classloaders and when tests use loadPipelineScriptForTest. Objects are loaded in a groovy classloader instead of the standard app classloader.

I am not sure whether this is to be 'expected' or there is an error in the way we have setup our project.

Steps to Reproduce

I did try to make a unit test but found it easier to provide a cut down project showing the error and a modified loadPipelineScriptForTest -> loadCustomPipelineScriptForTest that resolves the issue for me.
The project may be found at https://github.com/rnc/classloader-reproducer

How to set env variables while testing a groovy class.

class SampleClass implements Serializable{
	private def steps
	private def readObj
	
	SampleClass(def steps){
		this.steps = steps
	}

	void sampleMethod(String path){
		if(steps.env.NODE_NAME){
			steps.println("Jenkins env.NODE_NAME is set so it is inside a node")
			readObj = readYaml(file: path)
		}
	}
}

This is how I am testing:

class SampleTesting extends JenkinsPipelineSpecification{
	
	SampleClass obj
	
	void setup(){
     obj = new SampleClass(this)
 	}
	
	void "Testing sampleMethod"(){

		when:
		obj.sampleMethod("/tmp/xyz.yaml")

		then:
		1 * getPipelineMock("readYaml")("/tmp/xyz.yaml") >> {
			def map = [name: 'Gromit', likes: 'cheese', id: 1234] as LinkedHashMap
			return  map
		}

	}
}

This test will always fail as I can't find a way to set env variable. The only way avaiable is using loadPipelineScriptForTest()

def s = loadPipelineScriptForTest("path/to/pipelineScript")
s.getBinding().setVariable("env", [ NODE_NAME: "testNode" ])

but this is a groovy class and we cannot use loadPipelineScriptForTest()

Is there any way I can set env variable in such cases?

When asserting value returned from closure, getting null instead of the mocked value

I have a method that returns a closure that calls another method to get a string value and then return that back...

def getOpenshiftNamespace() {
    return openshift.withCluster() {
        openshift.project() <--- this returns "Test Project"
    }
}

When writing a test for this, i am having trouble getting the value out of the closure when mocking the openshift.project() method. I have the value inside the closure but it does not get returned out and assigned to the variable. This is what i have as far as a test. Seems like it should be a straight forward test..

  def "[getOpenshiftNamespace] will get openshift namespace" () {
    when:
      def namespace = utils.getOpenshiftNamespace()
    then:
      assert namespace == "Test Project" <--- namespace is null
      1 * getPipelineMock("openshift.withCluster")(_ as Closure)
      1 * getPipelineMock("openshift.project")() >> "Test Project"
  }

I can get the value if i return a string with openshift.withCluster such as

1 * getPipelineMock("openshift.withCluster")() >> {
    return "Test Project"
}

however i want to test the whole method and not just the outer layer.

Debugging tests

Hi, this is probably more of a spock/groovy question, but since I'm using your example POM I thought I'd ask here.

I was able to get unit tests working for my shared lib using your examples, and maven,
however I have a really hard time getting any debugger to work getting Error:Groovyc: While compiling fiori.pipeline: java.lang.RuntimeException: Error grabbing Grapes -- [unresolved dependency: com.google.guava#guava;19.0 .
I suspect it is caused by IntelliJ trying to debug using Java, but I'm not groovy expert.

Does Intellij debug your tests ok, or do use some different debugger, special setup?

Thanks in advance.

Assign value to explicitly mocked pipeline variable

@awittha Is it possible to assign a value to a pipeline variable that is mocked via explicitlyMockPipelineVariable or another way to assign a value to a pipeline variable without mocking it?

I am testing a class so it is being loaded in as a normal class and not as a script, so i don't have the ability to use getBinding().setVariable("variable", "value").

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class ExampleSpec extends JenkinsPipelineSpecification {
    def exampleClass

    def setup() {
        ExampleClass exampleClass = new ExampleClass()

        explicitlyMockPipelineVariable("variable")

        // Would like to set the value of "variable" 
    }

    ...
}

Thanks!

Cannot open URL when using loadPipelineScriptForTest

Expected Behavior

I expect that the script can be loaded for a test when it's located in the vars folder

Actual Behavior

I receive the following exception:

groovy.util.ResourceException: Cannot open URL: file:/Volumes/Workspace/code/jenkins-library/target/test-classes/varscommonPipeline.groovy

	at groovy.util.GroovyScriptEngine.getResourceConnection(GroovyScriptEngine.java:410)
	at groovy.util.GroovyScriptEngine.loadScriptByName(GroovyScriptEngine.java:552)
	at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.loadPipelineScriptForTest(JenkinsPipelineSpecification.groovy:730)
	at com.innogames.village.jenkins.pipeline.PipelineTest.setup(PipelineTest.groovy:10)

I load the pipeline the same way It's loaded in the examples:

class PipelineTest extends JenkinsPipelineSpecification {

  def commonPipeline

  def setup() {
    commonPipeline = loadPipelineScriptForTest("vars/commonPipeline.groovy")
  }
}

Steps to Reproduce

See above

Additional Information

I use the project within a shared library which I build with gradle.

The following screenshot is showing the debugger at the place where it fails.
Screenshot 2019-11-22 at 11 40 52

The used groovy runtime is 2.4.15

How can i mock a shared library that have multiple methods

Desired Behavior

Not sure how to mock and use in unit testing.

Please describe the new behavior the project should have.

Benefits

So that i can have single groovy file in vars and that one file have multiple methods.
eg
Logger.groovy

NotifyError(String)

NotifyMessage(String)

NotifySlack(MAP[] configuration)

Please list the benefits of updating the project to have the new behavior, e.g.

  1. Builds more quickly
  2. Enable compatibility with a new platform

loadPipelineScriptForTest() from vars -> java.lang.NoClassDefFoundError:

I want to write a unit test for the following groovy script, but it got an error when loadPipelineScriptForTEst() is called to load the scripts from vars:

ava.lang.NoClassDefFoundError: org/jenkinsci/plugins/workflow/steps/GeneralNonBlockingStepExecution
Caused by: java.lang.ClassNotFoundException: org.jenkinsci.plugins.workflow.steps.GeneralNonBlockingStepExecution
def call( _shaId ) {
    print("Getting PR info for ${_shaId}")
    def stringPayload = null
    withCredentials([string(credentialsId: 'BITBUCKET_READ_TOKEN', variable: 'token')]){
        stringPayload = sh(script:"""curl -H 'Authorization: Bearer ${token}' -H 'Content-Type: application/json' ${BITBUCKET_URL}rest/api/latest/projects/${projects}/repos/${repos}/commits/${_shaId}/pull-requests""",
                           returnStdout: true).trim()
    }
    print(stringPayload)
}

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class GetPullRequestInfoSpec extends JenkinsPipelineSpecification {

def PrInfo = null

def setup(){
    PrInfo = loadPipelineScriptForTest("vars/getPullRequestInfo.groovy")
}

def "No Pull Request"(){
    
}

}

jenkins-spock does not mock 'sh' when passing additional arguments

Expected Behavior

passing getPipelineMock("sh")([returnStdout:true], cmd) for the 'sh' step of workflow-basic-steps properly mocks and returns a mocked variable.

Actual Behavior

When passing additional arguments (returnStdout, returnStatus) nothing is mocked properly.

Steps to Reproduce

Mock a call with a extra variable and see that nothing is returned appropriately.

Testing parallel blocks

Hey,

I'm trying to test a pipeline with parallel stages, e.g.
parallel: 'lint' { // ... }, 'unit tests' { //... }

In the mocked invocations, I can see the mock for parallel -
getPipelineMock("parallel").call(['lint':pythonApiPipeline$_call_closure1$_closure5@5f254608, 'Unit tests':pythonApiPipeline$_call_closure1$_closure6@2eeb0f9b)

But I cannot figure how to make the test actually perform the closures, as right now it skips them.
Any help would be appreciated.

Document configuration property

I have seen the code
if( "true".equals( System.getProperty( "PipelineExtensionDetector.expandFailures" )
but I couldn't see any documentation related to it. Can this be documented please?

How to mock the setting of an environment variable?

I have the following step to set an environment variable:

env.CUSTOMER = 'BMT' 

When running the step during a unit test I got the following error message (reduced):

groovy.lang.MissingPropertyException: No such property: CUSTOMER for class: com.homeaway.devtools.jenkins.testing.PipelineVariableImpersonator
at [...]

Please tell me how to mock that step right.
Thank you in general for the excellent library and in advance for your help!

Mix Spock and JenkinsSpock

I would like to mix Spock and JenkinsSpock. i have some old tests written in Spock. My Plan is to slowly convert these to JenkinsSpock.
Unfortunately i doesn't go well when the JenkinsSpock tests are executed before the normal Spock tests.
It looks like the Specification class gets overridden by the JenkinsSpecification.
In my old tests i use an Expando class, which implements some pipeline steps as functions to mock the steps.
I pass this Expando as the WorkflowScript into class i wanna test.
So the JenkinsSpock mocks stuff that should'nt be mocked and the tests are failing.

The easiest solutions would be the enforce ordering of the tests, that Spock tests are always executed before the the JenkinsSpock tests.

Loading class as script to test

I have a class that i am trying to load in and write tests for but am getting a casting error, org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'com.example.Example@28f36e9d' with class 'com.example.Example' to class 'groovy.lang.Script'

Example.groovy

package com.example

class Example implements Serializable {
    Utils utils = new Utils()

    def execute() {
        return "Has executed"
    }
}

This class is throwing the error above.


However if i switch the class to something more like:

package com.example

def execute() {
    return "Has executed"
}

return this

The test does not throw the error above


This is how i am testing the class:

ExampleSpec.groovy

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class ExampleSpec extends JenkinsPipelineSpecification {
    def  example = null

    def setup() {
        example = loadPipelineScriptForTest("../../src/com/example/Example.groovy")
    }

    def "[Example] will return a string" () {
        when:
           def string = example.execute()
        then:
           assert string == "Has executed"
    }
}


Would you have any ideas on this problem? Not sure if there is something about the class itself of maybe something in the loadPipelineScriptForTest that doesn't allow for that type of class to be loaded in. Thanks!

How to mock sh(returnStatus: true, script: "")

How can I mock int tagExist = sh(returnStatus: true, script: "git tag -l | grep -w ${tagName}\$") ?
Error Message: myTest(MyTestSpec) Time elapsed: 0.973 s <<< ERROR! org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'null' with class 'null' to class 'int'. Try 'java.lang.Integer' instead at MyTestSpec.myTest

NullPointerException when chaining methods of mocked step

Expected Behavior

The following line of a custom pipeline step should be mockable and produce an invocation on the mock object.

docker.image("node").inside("-u root") { sh "node -v" }

Actual Behavior

But instead of an invocation on the mock object the following exception occurred:

java.lang.NullPointerException: Cannot invoke method inside() on null object
        at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91)
        at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:47)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
        at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:34)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:136)
        at withDockerNode.call(withDockerNode.groovy:16)
        at withDockerNode.call(withDockerNode.groovy)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225)
        at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1125)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
        at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:947)
        at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:930)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:181)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeClosure(ScriptBytecodeAdapter.java:587)
        at WithDockerNodeSpec.$spock_feature_1_1(WithDockerNodeSpec.groovy:26)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:200)
        at org.spockframework.runtime.model.MethodInfo.invoke(MethodInfo.java:113)
        at org.spockframework.runtime.BaseSpecRunner.invokeRaw(BaseSpecRunner.java:484)
        at org.spockframework.runtime.BaseSpecRunner.invoke(BaseSpecRunner.java:467)
        at org.spockframework.runtime.BaseSpecRunner.runFeatureMethod(BaseSpecRunner.java:408)
        at org.spockframework.runtime.BaseSpecRunner.doRunIteration(BaseSpecRunner.java:322)
        at org.spockframework.runtime.BaseSpecRunner$6.invoke(BaseSpecRunner.java:306)
        at org.spockframework.runtime.BaseSpecRunner.invokeRaw(BaseSpecRunner.java:484)
        at org.spockframework.runtime.BaseSpecRunner.invoke(BaseSpecRunner.java:467)
        at org.spockframework.runtime.BaseSpecRunner.runIteration(BaseSpecRunner.java:285)
        at org.spockframework.runtime.BaseSpecRunner.initializeAndRunIteration(BaseSpecRunner.java:275)
        at org.spockframework.runtime.BaseSpecRunner.runSimpleFeature(BaseSpecRunner.java:266)
        at org.spockframework.runtime.BaseSpecRunner.doRunFeature(BaseSpecRunner.java:260)
        at org.spockframework.runtime.BaseSpecRunner$5.invoke(BaseSpecRunner.java:243)
        at org.spockframework.runtime.BaseSpecRunner.invokeRaw(BaseSpecRunner.java:484)
        at org.spockframework.runtime.BaseSpecRunner.invoke(BaseSpecRunner.java:467)
        at org.spockframework.runtime.BaseSpecRunner.runFeature(BaseSpecRunner.java:235)
        at org.spockframework.runtime.BaseSpecRunner.runFeatures(BaseSpecRunner.java:185)
        at org.spockframework.runtime.BaseSpecRunner.doRunSpec(BaseSpecRunner.java:95)
        at org.spockframework.runtime.BaseSpecRunner$1.invoke(BaseSpecRunner.java:81)
        at org.spockframework.runtime.BaseSpecRunner.invokeRaw(BaseSpecRunner.java:484)
        at org.spockframework.runtime.BaseSpecRunner.invoke(BaseSpecRunner.java:467)
        at org.spockframework.runtime.BaseSpecRunner.runSpec(BaseSpecRunner.java:73)
        at org.spockframework.runtime.BaseSpecRunner.run(BaseSpecRunner.java:64)
        at org.spockframework.runtime.Sputnik.run(Sputnik.java:63)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:273)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:159)
        at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:383)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:344)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:417)

Additional Information

I added the following dependency to get the docker step sources

<dependency>
  <!-- provides docker() step -->
  <groupId>org.jenkins-ci.plugins</groupId>
  <artifactId>docker-workflow</artifactId>
  <version>LATEST</version>
  <scope>test</scope>
</dependency>

java -version

java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

Pipeline logic is not executed using groovy-cps

Hi Guys,

I'm trying to find a testing framework that actually can execute the pipeline logic in the same way as workflow-cps-plugin do to catch the next bug in jenkins pipelines: JENKINS-59911

Expected Behavior

...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running DefaultPipelineSpec
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.271 s - in DefaultPipelineSpec
[INFO] Running DeployerSpec
DEBUG: CPS results: null, 6
DEBUG: CPS results: null, 6
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.093 s - in DeployerSpec
...

According to the bug, the logic should print "null, 6" and CPS tests of JenkinsPipelineUnit is almost doing the job: jenkinsci/JenkinsPipelineUnit#159 - but contains a bug with .find/.findAll and the other closure-based functions and its CPS testing is useless on a complex pipelines.

Actual Behavior

...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running DefaultPipelineSpec
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.271 s - in DefaultPipelineSpec
[INFO] Running DeployerSpec
DEBUG: CPS results: 6, 6
DEBUG: CPS results: 6, 6
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.093 s - in DeployerSpec
...

jenkins-spock printing "6, 6" - that could mean anything, but for sure it's not the expected behavior - because of the previously described issue in groovy-cps.

Steps to Reproduce

  1. Modify shared-library example with the next changes:
    diff --git a/examples/shared-library/vars/Deployer.groovy b/examples/shared-library/vars/Deployer.groovy
    index 1e594fd..d399249 100644
    --- a/examples/shared-library/vars/Deployer.groovy
    +++ b/examples/shared-library/vars/Deployer.groovy
    @@ -1,6 +1,10 @@
     import com.example.SharedLibraryConstants
    
     def call( _env ) {
    +    def a = 4
    +    def b = 3
    +    a = b = 6
    +    System.out.println("DEBUG: CPS results: ${a}, ${b}")
    
         if( _env == "test" ) {
             sshagent(["test-ssh"]) {
  2. Run mvn clean verify in examples/shared-library

Additional Information

It seems like jenkins-spock actually not using groovy-cps to execute the logic.

explicitlyMockPipelineStep is needed even with getPipelineMock

I'm not sure if my setup is incorrect or not (I tried to copy from the examples as much as possible), but in my setup block, before I could do getPipelineMock, I have to explicitly mock that pipeline step first. That doesn't seem to be the case in the example.

For instance, if I do

setup:
  getPipelineMock('sh')([returnStdout: true, script: 'echo foo']) >> 'foo'

I will get this error:

java.lang.IllegalStateException:
There is no pipeline step mock for [sh].
        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('sh') in your test's setup: block.

What I need to do instead is

setup:
  explicitlyMockPIpelineStep('sh')
  getPipelineMock('sh')([returnStdout: true, script: 'echo foo']) >> 'foo'

What might I be missing here?

Gradle example

Desired Behavior

I'd like to have an example implementation using gradle (specifically gradlew) over maven.

Benefits

Gradle is generally much more flexible and easier to consume for users. Maven is much harder to integrate with these days. A major benefit we have seen with gradle is to provide foundational plugins that provide good default config - one benefit of this could be to provide a plugin that specifies the recommended dependencies (e.g. jenkins core version) for a wide set of pipeline libraries.

mocking jenkins.model.Jenkins

Hi,

I am using jenkins.model.Jenkins.instance.getNode('node').getComputer().setTemporarilyOffline(true), but I ran into this error when running unit test.

...
java.lang.NullPointerException: Cannot get property 'Jenkins' on null object```

I don't know how to resolve it.
My question is how to mock Jenkins object?

Problem Compiling Source Code

Expected Behavior

mvn compile should work without error.

Actual Behavior

I ran mvn compile at two different directories.
From jenkins-spock directory:
[ERROR] Failed to execute goal on project jenkins-spock: Could not resolve dependencies for project com.homeaway.devtools.jenkins:jenkins-spock:jar:yes: Failed to collect dependencies at org.jenkins-ci.main:jenkins-core:jar:2.102: Failed to read artifact descriptor for org.jenkins-ci.main:jenkins-core:jar:2.102: Could not transfer artifact org.jenkins-ci.main:jenkins-core:pom:2.102 from/to jenkins-releases (http://repo.jenkins-ci.org/releases): Connect to repo.jenkins-ci.org:80 [repo.jenkins-ci.org/130.211.20.35] failed: Connection timed out: connect -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal on project jenkins-spock: Could not resolve dependencies for project com.homeaway.devtools.jenkins:jenkins-spock:jar:yes: Failed to collect dependencies at org.jenkins-ci.main:jenkins-core:jar:2.102

From shared-library directory
[ERROR] Failed to execute goal on project jenkinsfile-test-shared-library: Could not resolve dependencies for project com.example:jenkinsfile-test-shared-library:jar:0.0.0-SNAPSHOT: Failed to collect dependencies at org.jenkins-ci.main:jenkins-core:jar:2.102: Failed to read artifact descriptor for org.jenkins-ci.main:jenkins-core:jar:2.102: Could not transfer artifact org.jenkins-ci.main:jenkins-core:pom:2.102 from/to jenkins-releases (http://repo.jenkins-ci.org/releases): Connect to repo.jenkins-ci.org:80 [repo.jenkins-ci.org/130.211.20.35] failed: Connection timed out: connect -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal on project jenkinsfile-test-shared-library: Could not resolve dependencies for project com.example:jenkinsfile-test-shared-library:jar:0.0.0-SNAPSHOT: Failed to collect dependencies at org.jenkins-ci.main:jenkins-core:jar:2.102

Steps to Reproduce

The mvn compile was performed on a Windows box.

  1. Install Java JDK 8
  2. Install Groovy 2.5.2
  3. Install Maven on Windows
  4. Clone Jenkins-Spock
  5. Run mvn compile at the two directories above.

Additional Information

I am beginning to suspect that I don't need to do the build myself to use Jenkins-Spock. If so, can you provide me with some pointers? I am still trying to make sense of the Java ecosystem.

Unable to mock the setting of an environment variable

Attempting to test the following code:

def method5() {
    def item1 = env.ITEM1 ?: 'default'
    def item2 = "value-${item1}"
    env.setProperty('ITEM2', item2)
    item2
}

With the following unit test:

    def 'test method5'() {
        setup:
            explicitlyMockPipelineVariable('env')
            getPipelineMock('env.getProperty').call('ITEM1') >> 'item1Value'
        expect:
            myExample.method5() == 'value-item1Value'

I continue to get the following error:

groovy.lang.MissingPropertyException: No such property: ITEM2 for class: com.homeaway.devtools.jenkins.testing.PipelineVariableImpersonator

When testing it a bit differently using the following method:

    def 'test method5-2'() {
        setup:
            explicitlyMockPipelineVariable('env')
        when:
            myExample.method5()
        then:
            1 * getPipelineMock('env.getProperty').call('ITEM1') >> 'item1Value'
            1 * getPipelineMock('env.setProperty').call('ITEM2', 'value-item1Value')
    }

I get the same error.

I've successfully unit tested cases for retrieving environment variables, but code that includes setting using 'setProperty' fails with the error above.

I'm thinking the best route is to create my own mock object for environment, but I'm not sure what class to Mock i.e. what the Environment API class is in Groovy.

            def mockEnv = Mock(Object)
            myExample.getBinding().setVariable('env', mockEnv)

Any advice is appreciated. Thanks.

Loads scripts found in src folder.

Desired Behavior

I would like the ability to load a groovy script found under the src folder. These files would normally be created as a new instance of the class and the functions called on that instance. For example, I have a file at src/my/company/devops/GitUtils.groovy that contains helper functions. Something like:

def isGitRepo(gitRepoPath = "${env.WORKSPACE"}) {
    return sh("cd ${gitRepoPath} && git rev-parse --git-dir")
}

return this

Notice I have the sh step as well as a global variable referenced. In a pipeline, I am able to create a new GitUtils and call isGitRepo and it works.

But currently, loading this class as a script while it exists in a package structure does not work. If I try:

loadPipelineScriptForTest("src/my/company/devops/GitUtils.groovy")

I get a groovy.util.ResourceException, because the file does not exist at -
target/classes/src/my/company/devopsGitUtils.groovy
src/test/resource/src/my/company/devopsGitUtils.groovy
src/main/resource/src/my/company/devopsGitUtils.groovy

Notice the missing forward slash between the final folder and file name.

But if I move this class to the vars folder, removing package information, I am able to test it with no issue.

Benefits

Allow all code contained in a shared library to be testable.

Mocking a step from a plugin does not work when testing vars/*.groovy

Expected Behavior

If i try to mock a step from a plugin when executing a step from vars/*.groovy it always executes the original step of the plugin. e.g. i'm trying to override isUnix() from the workflow-basic-steps plugin and it always executes the original step.

Actual Behavior

That the override would work.

Steps to Reproduce

vars/exec.groovy:

void call(String command) {
    if (isUnix()) {
	    sh command
    } else{
	    bat command
    }
}

ExecTest.groovy:

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class ExecTest extends JenkinsPipelineSpecification {

    def Exec

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

    def "Test on Windows"() {
        setup:
            getPipelineMock('isUnix')(_) >> { return false }

        when:
            Exec('ls')
        then:
            1 * getPipelineMock('isUnix') ()
            1 * getPipelineMock('bat') ('ls')
    }

    def "Test on Linux"() {
        setup:
            getPipelineMock('isUnix')(_) >> { return true }

        when:
            Exec('ls')
        then:
            1 * getPipelineMock('isUnix') ()
            1 * getPipelineMock('sh') ('ls')
    }
}

How to test @Library?

Greetings!

I have a pipeline with @Library("pipeline) _ . If I try to test this pipeline, I get the following error:

unable to resolve class Library , unable to find class for annotation
@ line 3, column 1.
@Library("pipeline") _
^

So far I was unable to resolve this error myself, find an answer in the documentation or the other issues posted here to resolve this error.

Is @Library even supported? If yes, how do I specify the test such that the error is resolved?

Thank you in advance!

Best Regards,
Oliver

How to mock a library class

I have a custom step that imports a library class, instantiates the class and then calls a method from that class. In my test, i was wondering how i would mock that class..

vars/step.groovy

import com.example.Utils

def call(Map config=[:]){
    utils = new Utils()
    return utils.method()
}

Utils.groovy

package com.example

def method(param) {
    // Does a thing
}

StepSpec.groovy

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification
import com.example.Utils

public class StepSpec extends JenkinsPipelineSpecification {

    def step = null

    def setup() {
	step = loadPipelineScriptForTest("vars/step.groovy")
        // How do i mock Utils here so that i don't have to worry about what happens inside the 
        // Utils.method
    }

    def "will call step with passed command" () {
	when:
	    step("param")
	then:
	    assert true
    }
}

how to stub httpRequest from HTTP Request Plugin

Newbie to jenkins-spock, spock and groovy.

Need some help stubbing httpRequest from HTTP Request Plugin. The jar dependency is org.jenkins-ci.plugins:http_request:1.8.26.

My usage looks like:

class Http2 implements Serializable {
    def verify() {
        def response = httpRequest 'http://abc.com/def'
        echo "${response.content}"
    }
}

I have tried writing a spec like the following:

    def 'http2 mock response' () {
        given:
        Http2 http2 = new Http2()
        def response = [:]
        response.content = "{'text': 'json response'}"
        getPipelineMock('httpRequest.call')('http://abc.com/def') >> response

        when:
        http2.verify()

        then:
        1 * getPipelineMock('httpRequest')(*_)
    }

but I get the exception:

org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Mock for type 'Closure' named 'getPipelineMock("httpRequest")'' with class 'groovy.lang.Closure$$EnhancerByCGLIB$$cda1b554' to class 'com.homeaway.devtools.jenkins.testing.PipelineVariableImpersonator'

	at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.getPipelineMock(JenkinsPipelineSpecification.groovy:742)

Changing the getPipelineMock call to:

getPipelineMock('httpRequest')('http://abc.com/def') >> response

gives me the following stacktrace:

[2020-05-24 20:09:49,149](main)([]) DEBUG JenkinsPipelineSpecification - TEST FIXTURE intercepted & redirected a call:
	Test         : class com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification
	Note         : pipeline step 
	Via          : class ca.utoronto.easi.jenkins.Http2.methodMissing
	    (types)  : Class.methodMissing
	Invocation   : class ca.utoronto.easi.jenkins.Http2.httpRequest([http://abc.com/def])
	    (types)  : Class.httpRequest(Object[])
	Forwarded To : Mock for type 'Closure' named 'getPipelineMock("httpRequest")'.call(http://abc.com/def)
	    (types)  : Closure$$EnhancerByCGLIB$$cda1b554.call(String)


java.lang.NullPointerException: Cannot get property 'content' on null object

One slight twist is that I am on gradle, not maven.

Any help is appreciated.

Use of @grab in class causing errors

When using @grab in my class files results in an error when running tests for those files.

[INFO] Using Groovy 2.4.11 to perform generateStubs.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.790 s
[INFO] Finished at: 2019-03-05T11:15:22-05:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.gmavenplus:gmavenplus-plugin:1.6.1:generateStubs (groovy) on project: Error occurred while calling a method on a Groovy class from classpath.: InvocationTargetException: org/apache/ivy/Ivy: org.apache.ivy.Ivy -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

@awittha any ideas on why this would be failing? When i remove all @grab references the tests go through. Thanks!

Unable to run example tests due to dependency resolution exception

Expected Behavior

The example tests to be executed.

Actual Behavior

The build fails with the following output:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------< com.example:jenkinsfile-test-shared-library >-------------
[INFO] Building jenkinsfile-test-shared-library 0.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The POM for org.jenkins-ci.main:jenkins-core:jar:2.102 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins.workflow:workflow-step-api:jar:2.10 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins.workflow:workflow-cps:jar:2.36 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci:symbol-annotation:jar:1.10 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins.workflow:workflow-durable-task-step:jar:2.21 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins:slack:jar:2.3 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins:pipeline-stage-step:jar:2.3 is missing, no dependency information available
[WARNING] The POM for org.jenkins-ci.plugins:ssh-agent:jar:1.16 is missing, no dependency information available
Downloading from central: https://repo.maven.apache.org/maven2/org/jenkins-ci/plugins/workflow/workflow-cps-global-lib/2.10/workflow-cps-global-lib-2.10.pom
[WARNING] The POM for org.jenkins-ci.plugins.workflow:workflow-cps-global-lib:jar:2.10 is missing, no dependency information available
Downloading from central: https://repo.maven.apache.org/maven2/org/jenkins-ci/plugins/workflow/workflow-cps-global-lib/2.10/workflow-cps-global-lib-2.10.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.053 s
[INFO] Finished at: 2018-09-20T08:03:00-07:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project jenkinsfile-test-shared-library: Could not resolve dependencies for project com.example:jenkinsfile-test-shared-library:jar:0.0.0-SNAPSHOT: The following artifacts could not be resolved: org.jenkins-ci.main:jenkins-core:jar:2.102, org.jenkins-ci.plugins.workflow:workflow-step-api:jar:2.10, org.jenkins-ci.plugins.workflow:workflow-cps:jar:2.36, org.jenkins-ci:symbol-annotation:jar:1.10, org.jenkins-ci.plugins.workflow:workflow-durable-task-step:jar:2.21, org.jenkins-ci.plugins:slack:jar:2.3, org.jenkins-ci.plugins:pipeline-stage-step:jar:2.3, org.jenkins-ci.plugins:ssh-agent:jar:1.16, org.jenkins-ci.plugins.workflow:workflow-cps-global-lib:jar:2.10: Failure to find org.jenkins-ci.main:jenkins-core:jar:2.102 in https://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
make: *** [test] Error 1

Steps to Reproduce

Please include a numbered list of steps that other people can use to recreate the "Actual Behavior."

  1. Install Maven (via brew)
  2. Clone repo
  3. cd examples/shared-library (similar failures in other directories)
  4. make test

Additional Information

sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65
mvn -v
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T11:33:14-07:00)
Maven home: /usr/local/Cellar/maven/3.5.4/libexec
Java version: 1.8.0_172, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre
Default locale: en_GB, platform encoding: UTF-8
OS name: "mac os x", version: "10.13.6", arch: "x86_64", family: "mac"
java -version
java version "1.8.0_172"
Java(TM) SE Runtime Environment (build 1.8.0_172-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

Examples: Dependency Missing

The examples fail build because of a missing dependency which is probably cached for you.

% mvn test
[ERROR] Failed to execute goal on project jenkinsfile-test-shared-library: Could not resolve dependencies for project com.example:jenkinsfile-test-shared-library:jar:0.0.0-SNAPSHOT: Failure to find org.connectbot.jbcrypt:jbcrypt:jar:1.0.0 in http://repo.jenkins-ci.org/releases was cached in the local repository, resolution will not be reattempted until the update interval of jenkins-releases has elapsed or updates are forced -> [Help 1]

Document Magic numbers

Desired Behavior

1 * getPipelineMock( "node" )("nodeType", _)
What does the 1 tell me? It's horrible to read. I can't find anything in the GroovyDoc except for some examples.
I played around and it looks like the number of invocations of this step. Maybe an Enum with speaking names like Once, Never would be better.

Benefits

Please list the benefits of updating the project to have the new behavior, e.g.

  1. Improve Readability

Failed to cast Closure of inner step

Expected Behavior

Given are the following to pipeline steps

mayFail.groovy

def call(String name, Closure body) {
  try {
    body()
  } catch(error) {
    echo "${name} crashed"
    throw error
  }
}

myStep.groovy

def call(String tag, Closure body) {
  mayFail('myStep') {
    echo tag
    body()
  }
}

and the corresponding test should run successful.

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class MyStepSpec extends JenkinsPipelineSpecification {
  def myStep = null

  def setup() {
    myStep = loadPipelineScriptForTest("vars/myStep.groovy")
  }

  def "the best test ever" () {
    when:
      myStep("superstar") {
         echo "hello world"
      }
    then:
      1 * getPipelineMock("mayFail")(["myStep", _ as Closure])
  }
}

Actual Behavior

The following error occurred when running the test:

org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '_' with class 'org.spockframework.lang.Wildcard' to class 'groovy.lang.Closure' due to: groovy.lang.GroovyRuntimeException: failed to invokeconstructor: public groovy.lang.Closure(java.lang.Object) with arguments: [*_] reason: java.lang.InstantiationException
        at MyStepSpec.the best test ever(MyStepSpec.groovy:16)

Additional Information

java -version

java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

Grab usage documentation

Desired Behavior

While I can see PR 26 this does not seem to be merged into master. I think it would be useful if the documentation described this in master ?

As a potential alternative I would also like to suggest that using the grab-dependency-populator maven extension avoids having to duplicate the httpclient definition in the pom - this makes maintenance simpler, avoid potential mistakes between the grab definition and pom definition.

RuntimeException: Unknown constant pool tag 60 in classfile com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class

Hey, i cannot test my project, which is currently very simple. Could you take a look at that ? Thanks !

Expected Behavior

The project builds

Actual Behavior

java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Unknown constant pool tag 60 in classfile com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class (element size unknown, cannot continue reading class. Please report this on the FastClasspathScanner GitHub page.
	at io.github.lukehutch.fastclasspathscanner.FastClasspathScanner.scan(FastClasspathScanner.java:1644)
	at io.github.lukehutch.fastclasspathscanner.FastClasspathScanner.scan(FastClasspathScanner.java:1678)
	at io.github.lukehutch.fastclasspathscanner.FastClasspathScanner.scan(FastClasspathScanner.java:1704)
	at com.homeaway.devtools.jenkins.testing.WholeClasspathPipelineExtensionDetector.getClassesWithAnnotationOfTypeInPackage(WholeClasspathPipelineExtensionDetector.java:97)
	at com.homeaway.devtools.jenkins.testing.APipelineExtensionDetector.getPipelineSteps(APipelineExtensionDetector.java:81)
	at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.setupSpec(JenkinsPipelineSpecification.groovy:999)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Unknown constant pool tag 60 in classfile com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class (element size unknown, cannot continue reading class. Please report this on the FastClasspathScanner GitHub page.
	at io.github.lukehutch.fastclasspathscanner.utils.InterruptionChecker.executionException(InterruptionChecker.java:86)
	at io.github.lukehutch.fastclasspathscanner.utils.WorkQueue.runWorkLoop(WorkQueue.java:199)
	at io.github.lukehutch.fastclasspathscanner.utils.WorkQueue$1.call(WorkQueue.java:147)
	at io.github.lukehutch.fastclasspathscanner.utils.WorkQueue$1.call(WorkQueue.java:144)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.RuntimeException: Unknown constant pool tag 60 in classfile com/ibm/icu/impl/data/LocaleElements_zh__PINYIN.class (element size unknown, cannot continue reading class. Please report this on the FastClasspathScanner GitHub page.
	at io.github.lukehutch.fastclasspathscanner.scanner.ClassfileBinaryParser.readClassInfoFromClassfileHeader(ClassfileBinaryParser.java:646)
	at io.github.lukehutch.fastclasspathscanner.scanner.ClasspathElement.parseClassfiles(ClasspathElement.java:297)
	at io.github.lukehutch.fastclasspathscanner.scanner.Scanner$6.processWorkUnit(Scanner.java:473)
	at io.github.lukehutch.fastclasspathscanner.scanner.Scanner$6.processWorkUnit(Scanner.java:467)
	at io.github.lukehutch.fastclasspathscanner.utils.WorkQueue.runWorkLoop(WorkQueue.java:187)

Steps to Reproduce

Please include a numbered list of steps that other people can use to recreate the "Actual Behavior."

  1. Create a jenkinsfile
pipeline {
    agent none
    stages {
        stage('test parameters') {
            steps {
                sh 'echo "hello"'
            }
        }
    }
}
  1. Create a test loading this file
import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class CheckParameterTest extends JenkinsPipelineSpecification  {

    def "test" () {
        setup:
            def jenkinsFile = loadPipelineScriptForTest("test/pipelines/checkParametersTest.jenkins")
        when:
            jenkinsFile.run()
        then:
            1 * getPipelineMock("node")("hello", _)
    }
}

  1. Launch a gradlew clean test

Additional Information

My build.gradle. :

group 'com.talend.jenkins.maintenance.sharedLib'
version 'O.1-SNAPSHOT'

allprojects {
    apply plugin: 'groovy'
    sourceCompatibility = 1.9
    targetCompatibility = 1.9
}

buildscript {
    repositories {
        mavenCentral()
    }
}

repositories {
    mavenCentral()
    maven {
      url "https://repo.jenkins-ci.org/releases/"
    }
    maven {
      url "https://repo.jenkins-ci.org/public/"
    }
}

dependencies {
    compile group: 'com.cloudbees', name: 'groovy-cps', version: '1.24'
    compile group: 'org.codehaus.groovy', name: 'groovy-json', version: '2.5.2'

    testCompile 'com.homeaway.devtools.jenkins:jenkins-spock:2.1.0'
    testCompile(group: 'org.jenkins-ci.plugins.workflow', name: 'workflow-step-api', version:'2.22') {
      artifact {
        name = "workflow-step-api"
        type = 'jar'
      }
    }
    testCompile "org.jenkins-ci:symbol-annotation:1.10"
    testCompile "junit:junit:4.12"
    testCompile "javax.servlet:javax.servlet-api:3.1.0"
    testCompile "org.connectbot.jbcrypt:jbcrypt:1.0.0"
    testCompile "org.jenkins-ci.main:jenkins-core:2.102"
    testCompile "io.github.lukehutch:fast-classpath-scanner:3.1.6"
}

sourceSets {
    main {
        groovy {
            srcDirs = ['src', 'vars']
        }
    }
    test {
        groovy {
            srcDirs = ['test']
        }
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.