I'm new to groovy. Been using it for a few weeks. I wrote a class that is used in Jenkins as a shared library. The simple methods and structure works fine.
But I'm planning to make it bigger, so I want to add unit tests to it. Right now I just want to import the class in a script, and run some asserts. But I get the following error:
unable to resolve class NonCPS , unable to find class for annotation
@ line 51, column 2.
@NonCPS
The annotation is required by Jenkins.
The library (which again works) something like:
class MyTestClass implements Serializable {
MyTestClass() { }
@NonCPS
def testMethod() { }
in the script I call
import com.blabla.MyTestClass
...
test = new MyTestClass()
if I just comment the annotation for testing it works. I've been searching around and have seem some pretty cool projects to test Jenkins pipelines including their shared libraries, but I'm a little confused right now I would like to just get this tests before using more sophisticated things.
From the groovy documentation, I understand that the annotation should go in a class definition and not a method (is this correct?) but then how is it that this works when being run on Jenkins?
When you put @NonCPS on a method, Jenkins will execute the entire method in one go without the ability to pause. Also, you're not allowed to reference any pipeline steps or CPS transformed methods from within an @NonCPS annotated method.
The Jenkinsfile is written using the Groovy Domain-Specific Language and can be generated using a text editor or the Jenkins instance configuration tab. The Declarative Pipelines is a relatively new feature that supports the concept of code pipeline.
On Linux, BSD, and Mac OS (Unix-like) systems, the sh step is used to execute a shell command in a Pipeline. Jenkinsfile (Declarative Pipeline) pipeline { agent any stages { stage('Build') { steps { sh 'echo "Hello World"' sh ''' echo "Multiline shell steps works too" ls -lah ''' } } } }
If you want to compile your shared library outside Jenkins runtime environment then you have to add groovy-cps dependency to your classpath and add an import:
import com.cloudbees.groovy.cps.NonCPS
to the class that uses @NonCPS
annotation. It does not break anything - Jenkins when prepares runtime environment for Jenkinsfile execution it adds the same import and when this import is present it does not make any collision.
I would also recommend using Gradle to define dependencies and build your shared library on CI server. The minimum build.gradle
file may look like this:
apply plugin: 'groovy'
apply plugin: 'idea'
repositories {
mavenCentral()
}
sourceSets {
main {
groovy {
srcDirs = ['src', 'vars']
}
}
test {
groovy {
srcDirs = ['test']
}
}
}
dependencies {
compile 'com.cloudbees:groovy-cps:1.22'
compile 'org.codehaus.groovy:groovy-all:2.4.12'
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
}
It uses src
and vars
as a source files directories and test
as a unit test directory. My shared library directory structure usually looks like this:
├── build.gradle
├── Jenkinsfile
├── src
│ └── com
│ └── foo
├── test
│ ├── com
│ │ └── foo
│ └── vars
│ └── myVarSpec.groovy
└── vars
└── myVar.groovy
In this example we use 2 compile time dependencies - groovy-cps
and groovy-all
, and Spock Framework for unit tests. Thanks to this you can run unit tests inside your IDE as well as on CI server with gradle test
command.
When running a simple unit tests is not enough, consider using JenkinsPipelineUnit library that allows you test your Jenkinsfiles including shared libraries. This is a great library that speeds up development and you can test your pipeline behavior in just a couple of milliseconds. This library works with JUnit, however there is some 3rd party wrapper that brings this library to Spock (I haven't test it though).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With