Open MichaelJKar opened 2 years ago
We're hitting a similar issue that we believe has the same root cause.
We're are trying to write a test that includes the pullRequest global variable form the pipeline-github-plugin. We are mocking it out with the following call
binding.setVariable('pullRequest', new PullRequestMock())
This works as expected when executing the following test
pipeline {
stages {
stage('test pullRequest global variable') {
steps {
script {
pullRequest.comment("foobar")
}
}
}
}
}
And also works as expected from the first level of our library
pipeline {
stages {
stage('test pullRequest global variable') {
steps {
script {
jenkinslibrary.commentOnPullRequest("foobar")
}
}
}
}
}
Where there exists a vars/jenkinslibrary.groovy
file with the following method
def commentOnPullRequest(String comment) {
pullRequest.comment(comment)
}
However, if we try to access that global variable inside another groovy class - for example src/pullRequestUtils.groovy
def commentOnPullRequest(String comment) {
def prUtils = new com.jenkinslibrary.pullRequestUtils()
prUtils.comment(comment)
}
def comment(String comment) {
pullRequest.comment(comment)
}
The execution fails with the following error
groovy.lang.MissingPropertyException: No such property: pullRequest for class: com.jenkinslibrary.pullRequestUtils
at app//groovy.lang.MetaClassImpl.invokeStaticMissingProperty(MetaClassImpl.java:1019)
at app//groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:2862)
at app//groovy.lang.MetaClassImpl.setProperty(MetaClassImpl.java:3854)
It looks like the global variable is not available inside the groovy classes, similar to params above. Mocking methods from plugins works as expected, just not global variables.
This code runs fine in our actual jenkins pipelines, i.e. we can access the global pullRequest variable from inside the class.
We are currently working around issue by adding a simple method that returns the pullRequest object inside our class, and mocking that as part of the test
def pullRequestDetails() {
return pullRequest
}
and then in the test file
helper.registerAllowedMethod('pullRequestDetails', [], { return new PullRequestMock() })
Which allows us to access the object
@MichaelJKar does the above workaround work for you? Can this issue be closed?
@nre-ableton We'd prefer not to use the workaround forever if possible. It's okay for this situation, but it requires you to set the same mock in two places if you want to use the pullRequest
object (or the params
object in @MichaelJKar's case) in both the pipeline and the library class. It also requires that you to add extra methods to your library classes to facilitate any testing that involves parameters or global plugin objects, which isn't ideal.
Indeed, the work around is not a good solution long term, and could cause issues as it forces changes in actual code just to make the tests work. It is also something a developer would waste time debugging if they do not know it is an issue of the testing framework.
On closer inspection, what if you pass the script context to the library? That is:
Jenkinsfile:
#!groovy
@Library('lib')
import LibClass
pipeline {
stages {
stage('Test stage') {
steps {
out = new LibClass(script: this).getX()
echo "Printing ${out}"
}
}
}
}
LibClass.groovy
:
class LibClass {
Object script
def getX() {
return script.params.X
}
}
This is issue is an interesting one. Based on @MichaelJKar reproduction steps I built a fix for this case. Sadly I do not know enough in order to say if this change can have some side effects in another (yet untested) cases.
Jenkins and plugins versions report
Environment
```text See minimal viable example below. Plugins should not play a role here. ```What Operating System are you using (both controller, and any agents involved in the problem)?
Windows
Reproduction steps
Jenkinsfile:
Test Class:
The shared library
lib
only contains one fileLibClass.groovy
with the following content:Expected Results
Actual Results
Loading shared library lib with version master
groovy.lang.MissingPropertyException: No such property: params for class: LibClass
Anything else?
When replacing the content of
getX()
withreturn env.X
, the test runs successfully and the output matches the expected output given above.I don't know why this differentiation between
env
andparams
exists in the testing framework, since these two maps are essentially equivalent concepts in Jenkins itself. If theenv
map is available in a class within the shared library, thenparams
should also be available.Note: I already commented on this problem in #402, but since the problem is not 100% the same, I opened a new ticket.