jenkinsci / JenkinsPipelineUnit

Framework for unit testing Jenkins pipelines
MIT License
1.54k stars 394 forks source link

How to deal with object references that keep changing in the callstack file #417

Open dclacher opened 2 years ago

dclacher commented 2 years ago

Hey guys and sorry if this has been asked before, but I couldn't quite find an answer for my problem.

I have a scripted pipeline in the vars folder called pipelineJenkinsCI.groovy that does the following while executing a step:

cPlugin.notifyBitbucket(this, [commitSha1: scmVars.GIT_COMMIT])

Now, cPlugin.groovy is an implicit class (not declared with the class block, it contains only static methods) in the src folder and notifyBitbucket is just a wrapper method for the original method of the Bitbucket plugin:

static def notifyBitbucket(def ctx, Map config = [:]) {
    try {
        notifyBitbucket(config) // the original plugin method
    } catch (Exception all) {
        if (jenkinsEnv != "dev") {
            ctx.currentBuild.result = 'FAILURE'
            error ('Fail to notifyBitbucket')
        }
    }
}

The problem is that I'm passing the Jenkins context this as the first parameter (def ctx) and when I create the callstack with the flag -Dpipeline.stack.write=true, it records the context's address in memory as follows:

cPlugin.notifyBitbucket(pipelineJenkinsCI@1953bc95, {commitSha1=XXX})

But, of course, once I run the test again without the flag, it fails because a new context is created and the address will be different during the script execution, and the match will never happen.

My test class looks like that:

...
class TestPipelineJenkinsCI extends BaseRegressionTestCPS {

    @Override
    @Before
    void setUp() throws Exception {
        super.setUp()
        mockCommon() // an imported method that registers many methods with helper.registerAllowedMethod
    }

    @Test
    void basicTest() throws Exception {
        Script script = loadScript("test/pipeline/pipelineJenkinsCI/jenkinsfiles/basic.jenkinsfile")

        runScript(script)

        assertJobStatusSuccess()
        testNonRegression("basicTest")
    }
}

I've read in other issues about mocking the context (this) but I'm not sure that would be a solution nor how to achieve that. Any ideas? Anyone with a similar problem?

Thank you.

dclacher commented 2 years ago

For now, I've implemented a workaround described here: https://github.com/jenkinsci/JenkinsPipelineUnit/issues/230#issue-628265576

mszalbach commented 2 years ago

I had a similar problem where this was always the Testname@MemoryAdress and keeps changing. It helped me to set the following to false:

helper.cloneArgsOnMethodCallRegistration = false

My call stack changed from

easyreleaser.execute(EasyReleaserTest@3c54ddec, ....

to

easyreleaser.execute(Script-EasyReleaserTest,

Not sure if this is the correct way.