es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Burlándose de pasos personalizados en archivos vars de Jenkins.

En mi biblioteca compartida de Jenkins tengo toneladas de archivos groovy en la carpeta /vars que definen pasos personalizados. Muchos de ellos tienen varios métodos definidos y un método en un archivo puede llamar a otro en el mismo archivo.

Estoy buscando una forma de simular estos métodos locales para poder probar unitariamente cada método, especialmente aquellos que llaman a otros, sin invocarlos realmente.

Imaginemos que la estructura es así:

// vars/step.groovy

def method1() {
  def someVar

  result = method2(someVar)

  if (result) { 
    echo 'ok' 
  }
  else { 
    echo 'no' 
  }
}

def method2(value) {

  if (value == 1) { 
    return true 
  }
  else { 
    return false 
  }
}

Obviamente, este es un ejemplo muy simplificado. Lo que necesito es una forma de simular method2 para que pueda probar method1 con result de true y false, sin invocar method2.

He intentado el patrón de helper.registerAllowedMethod, pero eso no parece aplicarse a los métodos locales. He intentado Mockito y Spock, pero parecen exagerados para lo que necesito y demasiado abrumadores para casos simples. También intenté definir los métodos localmente en el script de prueba con mock closures, pero no puedo encontrar el lugar correcto para hacer eso y / o la sintaxis correcta.

Espero que haya una manera de hacer algo como esto:

// test/com/myOrg/stepTest.groovy
import org.junit.*
import com.lesfurets.jenkins.unit.*
import com.lesfurets.jenkins.unit.BasePipelineTest
import static groovy.test.GroovyAssert.*

class stepTest extends BasePipelineTest {
  def step

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

  @Test
  void method1Test_true () {
    helper.registerAllowedMethod('method2', [], { true })

    result = step.method1()

    assert 'ok' == result
  }

  @Test
  void method1Test_false () {
    helper.registerAllowedMethod('method2', [], { false })

    result = step.method1()

    assert 'no' == result
  }
}

ACTUALIZACIÓN: una cosa que he notado recientemente es que, en la traza de pila, la función local method2 no se lista. Es como si la función local se instanciara “en línea” o de alguna manera en la que no se trata de una nueva llamada, la ejecución simplemente fluye hacia ella. No conozco el término técnico. Pero explica por qué el simulacro de method2 nunca es alcanzado: nunca se llama.

ACTUALIZACIÓN 2: Esto recibió cierta atención en el repositorio de GitHub de JPU.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Solución para burlar los métodos locales en la biblioteca compartida de Jenkins

    Si tiene una biblioteca compartida de Jenkins con varios archivos groovy en el directorio /vars que definen pasos personalizados, y desea burlar los métodos locales para probar unitariamente cada método, esto es lo que puede hacer:

    El problema

    Tiene un archivo groovy /vars/step.groovy con varios métodos. Un método en el archivo puede llamar a otro en el mismo archivo. Aquí hay un ejemplo:

    // vars/step.groovy
    
    def method1() {
      def someVar
    
      result = method2(someVar)
    
      if (result) { 
        echo 'ok' 
      }
      else { 
        echo 'no' 
      }
    
    }
    
    def method2(value) {
    
      if (value == 1) { 
        return true 
      }
      else { 
        return false 
      }
    
    }
    

    Quiere burlar method2 para que pueda probar method1 con result de ambos, true y false, sin invocar realmente method2.

    La solución

    Para burlar los métodos locales en la biblioteca compartida de Jenkins:

    1. Defina los métodos locales en su propia clase estática.
    2. Burlar la clase estática utilizando PowerMockito.
    3. Reemplace los métodos locales originales con los métodos burlados utilizando MetaClass.

    Así es cómo puede implementarlo:

    1. Defina los métodos locales en una clase estática

    Extraiga todos los métodos locales a una clase estática en el mismo archivo o en un archivo separado. Por ejemplo:

    // vars/step.groovy
    
    class StepMethods {
        static def method2(value) {
    
          if (value == 1) { 
            return true 
          }
          else { 
            return false 
          }
    
        }
    }
    
    def method1() {
      def someVar
    
      result = StepMethods.method2(someVar)
    
      if (result) { 
        echo 'ok' 
      }
      else { 
        echo 'no' 
      }
    }
    

    Observe que se movió method2 a una clase estática StepMethods y se cambió su invocación a StepMethods.method2(someVar).

    2. Burlar la clase estática utilizando PowerMockito

    Agregue la biblioteca PowerMockito a sus dependencias de prueba. Por ejemplo, en su archivo build.gradle:

    dependencies {
        testCompile("junit:junit:4.12")
        testCompile('org.powermock:powermock-api-mockito2:2.0.9')
        testCompile('org.powermock:powermock-module-junit4:2.0.9')
    }
    

    Luego, cree un archivo de prueba vars/stepTest.groovy:

    // test/vars/stepTest.groovy
    
    import org.powermock.api.mockito.PowerMockito
    
    class StepMethodsTest {
        def setup() {
            PowerMockito.mockStatic(StepMethods)
            PowerMockito.when(StepMethods.method2(1)).thenReturn(true)
            PowerMockito.when(StepMethods.method2(2)).thenReturn(false)
        }
    
        def 'method1Test true'() {
            when:
            result = method1()
    
            then:
            result == 'ok'
            PowerMockito.verifyStatic(MethodsStep, Times.atLeast(1));
        }
    
        def 'method1Test false'() {
            when:
            result = method1()
    
            then:
            result == 'no'
            PowerMockito.verifyStatic(MethodsStep, Times.atLeast(1));
        }
    
        // load the shared library method
        private def method1() {
            loadScript('vars/step.groovy').method1()
        }
    }
    

    Observe que estamos burlando la clase estática StepMethods utilizando la biblioteca PowerMockito. Estamos burlando StepMethods.method2 (1) para que devuelva verdadero y StepMethods.method2 (2) para que devuelva falso.

    También estamos creando dos casos de prueba, uno para cuando el resultado de method2 es verdadero y otro para cuando el resultado es falso.

    3. Reemplace los métodos locales con métodos burlados utilizando MetaClass

    En nuestro archivo de prueba, estamos cargando el método de la biblioteca compartida method1():

    // load the shared library method
    private def method1() {
      loadScript('vars/step.groovy').method1()
    }
    

    Antes de ejecutar nuestros casos de prueba, necesitamos reemplazar los StepMethods.method2() originales con los métodos burlados. Podemos hacerlo utilizando MetaClass:

    def setup() {
      PowerMockito.mockStatic(StepMethods)
      PowerMockito.when(StepMethods.method2(1)).thenReturn(true)
      PowerMockito.when(StepMethods.method2(2)).thenReturn(false)
    
      // replace StepMethods.method2() with our mock methods
      def step = loadScript('vars/step.groovy')
      def metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(step.class)
      metaClass.setProperty(step, 'method2', { value ->
        if (value == 1) {
          return true
        } else {
          return false
        }
      } as Closure)
    }
    

    En nuestro método setup(), primero estamos burlando la clase estática StepMethods. Luego, estamos reemplazando StepMethods.method2() con nuestros métodos burlados utilizando MetaClass. Observe que estamos definiendo el método burlado como un closure que toma un argumento value y devuelve verdadero o falso.

    Ahora podemos ejecutar nuestros casos de prueba y utilizar el StepMethods.method2() burlado.

Comments are closed.