jenkinsci / JenkinsPipelineUnit

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

mocking functions from a shared library #357

Open kerby82 opened 3 years ago

kerby82 commented 3 years ago

I'm testing groovy file that uses a function from a shared library

foo.groovy

def call(branch) {
  if (!git.fetch(branch)) {
    error "Error fetching ${branch}"
  }
}

I want to test it in isolation mode without the need of registering the SharedLibrary.

But I got the following error: groovy.lang.MissingPropertyException: No such property: git for class: foo

The test just does this:


class FooTest extends BaseTest {
  def foo 

  @Before
  void setUp() throws Exception {
    super.setUp()
    foo = loadScript("foo.groovy")
    helper.registerAllowedMethod('fetch', [String], {true})
  }

  @Test
  void fooTest() throws Exception {
    foo("master")
    printCallStack()
  }
nre-ableton commented 3 years ago

Is foo.groovy a singleton, for example, located in vars/foo.groovy in your library?

kerby82 commented 3 years ago

Yes is located in the vars but the git.fetch is located in a shared-library that is imported by the Jenkinsfile that is using foo. And my goal is to test foo in isolation mode.

nre-ableton commented 3 years ago

I think you need to mock the library import, in that case.

kerby82 commented 3 years ago

What do you mean? In this case my problem is I'm not able to mockup git.fetch because it does not find the property git, but I don't know how to set such a property so I can mock the fetch method.

microhod commented 3 years ago

I think because you're wanting to mock all of git you need to use binding.setVariable instead of helper.registerAllowedMethod. so you'd have:

  @Before
  void setUp() throws Exception {
    super.setUp()
    foo = loadScript("foo.groovy")
    binding.setVariable('git', new MockGit())
  }

Where MockGit can be just a mock class you can create in your test to mock out git.

Or you could probably just set it to a map with a closure if you don't want to bother with a whole mock class:

binding.setVariable('git', ['fetch': {s -> true}])
waytoharish commented 2 years ago

i have a Utility.groovy which is inside the src.util.data package and i am using in the var/MyJenkins.groovy . I am trying to mock the methods inside the Utility but not able to mock. Getting method not found error. i tried

microhod commented 2 years ago

If it's just a normal groovy class (rather than a built in Jenkins function/object) it can be mocked using standard mockito or groovy MockFor

waytoharish commented 2 years ago

its not class its a built in Jenkins function/object shared Library which have some of the method which i am trying to access in MyJenkins.groovy @microhod

microhod commented 2 years ago

Okay, I'm not sure I understand your use case, but there's three options as I understand it:

  1. Mock a groovy class using MockFor, that's for things like this (class can be inside or outside your shared library):
    
    package util.data

public class Utils { // ... }

```groovy
// MyJenkins.groovy
package var

import util.data.Utils

def call() {
  def utils = new Utils()
}
  1. Mock a jenkins function / object using binding.setVariable or helper.registerAllowedMethod (basically mocking a script outside of your shared library)
    
    // MyJenkins.groovy
    package var

def call() { sh("ls") // <- we'd want to mock out sh }

3. Mock a **groovy script** in the same Jenkins shared library - **I don't know if this is supported**, I've tried doing this before with both `binding.setVariable` and `helper.registerAllowedMethod` and neither seem to work. The workaround I've had to use is mocking out whatever the `utils.groovy` script does that you want mocked.
```groovy
// utils.groovy
package var

def utilMethod() {
  sh "..." // <- you have to mock 'sh' itself instead of mocking the whole of 'utils' itself
}
// MyJenkins.groovy
package var

def call() {
  utils.utilMethod()
}

I'm not a developer on this library btw, I've just used it quite a lot. So not 100% sure on what's officially supported.