使用Spock的Groovy共享库测试管道步骤方法

时间:2019-12-22 06:22:38

标签: jenkins groovy mocking jenkins-pipeline spock

我有一个共享库,它调用管道步骤方法(withCredentials)。我试图测试withCredentails方法在调用myMethodToTest时是否已通过sh脚本正确调用,但遇到错误:

 class myClass implements Serializable{
    def steps
    public myClass(steps) {this.steps = steps}

    public void myMethodToTest(script, String credentialsId) {
        steps.withCredentials([[$class: ‘UsernamePasswordMultiBinding’, credentialsId: "${credentialsId}", usernameVariable: ‘USR’, passwordVariable: ‘PWD’]]) {
             steps.sh """
                export USR=${script.USR}
                export PWD=${script.PWD}
                $mvn -X clean deploy
             """
          }
     }
}

//模拟

class Steps {
   def withCredentials(List args, Closure closure) {}
}

class Script {
    public Map env = [:]
}

//测试用例

def "testMyMethod"(){
        given:
        def steps = Mock(Steps)
        def script = Mock(Script)
        def myClassObj = new myClass(steps)
        script.env['USR'] = "test-user"

        when:
        def result = myClassObj.myMethodToTest(script, credId)

        then:
        1 * steps.withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "mycredId", usernameVariable: 'USR', passwordVariable: 'PWD']])  
        1 * steps.sh(shString)

        where:
        credId | shString
        "mycredId" | "export USR='test-user'"

//错误

Too few invocations for:

1 * steps.withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "mycredId", usernameVariable: ‘USR’, passwordVariable: ‘PWD’]])   (0 invocations)

Unmatched invocations (ordered by similarity):

1 * steps.withCredentials([['$class':'UsernamePasswordMultiBinding', 'credentialsId':mycredId, 'usernameVariable’:’USR’, 'passwordVariable':'PWD’]]

1 个答案:

答案 0 :(得分:1)

在测试和应用程序类的代码中,您都有很多细微的错误,而不是那么细微的错误。因此,让我提供一个新的MCVE,我在其中修复了所有问题并评论了测试中的一些关键部分:

package de.scrum_master.stackoverflow.q59442086

class Script {
  public Map env = [:]
}
package de.scrum_master.stackoverflow.q59442086

class Steps {
  def withCredentials(List args, Closure closure) {
    println "withCredentials: $args, " + closure
    closure()
  }

  def sh(String script) {
    println "sh: $script"
  }
}
package de.scrum_master.stackoverflow.q59442086

class MyClass implements Serializable {
  Steps steps
  String mvn = "/my/path/mvn"

  MyClass(steps) {
    this.steps = steps
  }

  void myMethodToTest(script, String credentialsId) {
    steps.withCredentials(
      [
        [
          class: "UsernamePasswordMultiBinding",
          credentialsId: "$credentialsId",
          usernameVariable: "USR",
          passwordVariable: "PWD"]
      ]
    ) {
      steps.sh """
        export USR=${script.env["USR"]}
        export PWD=${script.env["PWD"]}
        $mvn -X clean deploy
      """.stripIndent()
    }
  }
}
package de.scrum_master.stackoverflow.q59442086

import spock.lang.Specification

class MyClassTest extends Specification {
  def "testMyMethod"() {
    given:
    // Cannot use mock here because mock would have 'env' set to null. Furthermore,
    // we want to test the side effect of 'steps.sh()' being called from within the
    // closure, which also would not work with a mock. Thus, we need a spy.
    def steps = Spy(Steps)
    def myClass = new MyClass(steps)
    def script = new Script()
    script.env['USR'] = "test-user"

    when:
    myClass.myMethodToTest(script, credId)

    then:
    1 * steps.withCredentials(
      [
        [
          class: 'UsernamePasswordMultiBinding',
          credentialsId: credId,
          usernameVariable: 'USR',
          passwordVariable: 'PWD'
        ]
      ],
      _  // Don't forget the closure parameter!
    )
    // Here we need to test for a substring via argument constraint
    1 * steps.sh({ it.contains(shString) })

    where:
    credId     | shString
    "mycredId" | "export USR=test-user"
  }
}