JenkinsPipelineUnit icon indicating copy to clipboard operation
JenkinsPipelineUnit copied to clipboard

Mock imported classes

Open msabastian opened this issue 4 years ago • 4 comments

How can I mock imported classes (specifically the constructor) when using JPU?

Example:

vars/someCommand.groovy:

import com.mycompany.jenkins.SomeClass

def call(Map args = [:]) {
	def someArg = args.get('someArg')
	SomeClass someInstance = new SomeClass(someArg)
	...
}

test/vars/SomeCommandTest.groovy:

import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Test

class SomeCommandTest extends BasePipelineTest {
    def someCommand

    @Before
    void setUp() {
        super.setUp()
        someCommand = loadScript("vars/someCommand.groovy")
    }

    @Test
    void "should do some test"() {
        binding.setVariable('env', [
                ...
        ])
        //TODO: How to mock new SomeClass(someArg)
    }
}

msabastian avatar Oct 04 '20 21:10 msabastian

I'm not sure why you'd want to mock the class you are planning on testing, but maybe your pseudo-code above is missing something which would explain this.

Anyways, for the case that you describe, you can simply write standard unit tests for SomeClass and test the constructor by normal means:

package import com.mycompany.jenkins

import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Test

class SomeClassTest extends BasePipelineTest {
    def script

    @Before
    void setUp() {
        super.setUp()
        this.script = loadScript('test/resources/EmptyPipeline.groovy')
    }

    @Test
    void testConstruction() {
        SomeClass someClass = new SomeClass(someArg: 'foo')
        // assert that someClass was instantiated correctly...
    }
}

In the above example, an "empty" pipeline is loaded just to get a script context, which is typically passed to the classes during construction so they can run pipeline steps. That pipeline actually looks like this:

void execute() {
  node {
    stage('Empty') {}
  }
}

return this

If your class doesn't need to run pipeline steps, than you can skip this part. We don't bother testing the singletons (that is, vars/*.groovy files) because they are typically meant to be simple one-liners, and all the actual work is done in the classes.

You can see a working example of this in an open-source pipeline library published by my company.

Does this answer your question?

nre-ableton avatar Oct 05 '20 08:10 nre-ableton

@msabastian You can try to use groovy.mock.interceptor.MockFor see https://www.groovy-lang.org/testing.html#_mocking_and_stubbing

//Jenkinsfile
def job_cfg = new URL("https://jenkins.example.com/job/test/api?json").getText()
echo job_cfg
//TestCase
import groovy.mock.interceptor.MockFor
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Test

class TestExample extends BasePipelineTest {
    @Before void setUp() throws Exception {
        super.setUp()
    }
    @Test should_execute_without_errors() throws Exception {
        def mockURLContext = new MockFor(URL.class)
        mockURLContext.demand.getText { getClass().getResource('JobApiStub.json').text }
        mockURLContext.use {
            def script = runScript("Jenkinsfile")
        }
        printCallStack()
        assertJobStatusSuccess()
    }
}

stchar avatar Oct 07 '20 14:10 stchar

See also #141.

stchar avatar Oct 07 '20 15:10 stchar

@msabastian were you able to solve above problem with MockFor() ? I have the same issue.

Angelina1984 avatar Jan 29 '21 00:01 Angelina1984