如何将 Groovy 闭包变量注入 Spock 模拟谓词参数?

How to inject Groovy closure variable into Spock mock predicate argument?

我在 Spock 交互文档中发现了一个有趣的行:

http://spockframework.org/spock/docs/1.3/interaction_based_testing.html#_argument_constraints

约束的最后一行,闭包谓词示例:

1 * subscriber.receive({ it.size() > 3 && it.contains('a') })

我的问题是:在 Groovy 中是否有办法将此谓词作为变量传递?

我的测试环境代码:

class Something {
   Doer doer

   Something(Doer doer) {
      this.doer = doer
   }

   boolean doSth(x) {
      if (!doer.validate(x)) throw new RuntimeException()
      true
   }
}

class Doer {
   boolean validate(int x) {
      x == 2
   }
}

和测试代码:

   def "some test"() {
      given:
      def d = Mock(Doer)
      def s = new Something(d)

      when:
      s.doSth(2)

      then:
      1 * d.validate({ it == 2 }) >> true
   }

我想达到的目标:

def "some test"() {
      given:
      def d = Mock(Doer)
      def s = new Something(d)
      def myClosureVar = { ??? }

      when:
      s.doSth(2)

      then:
      1 * d.validate(myClosureVar) >> true
   }

闭包接受一个参数,如 it 所示,具有定义的值。该值是相应的方法参数。因此,无论您在交互之外定义什么闭包,您都需要确保交互将该参数移交给闭包,即您需要创建自己的(小而简单的)闭包来评估外部(可能更长、更复杂)闭包使用参数 it:

1 * d.validate({ myClosureVar(it) }) >> true

抱歉重复,但我总是喜欢完整的 MCVE 答案,这样您就可以轻松复制、粘贴、编译和 运行:

申请类:

package de.scrum_master.Whosebug.q60341734

class Doer {
  boolean validate(int x) {
    x == 2
  }
}
package de.scrum_master.Whosebug.q60341734

class Something {
  Doer doer

  Something(Doer doer) {
    this.doer = doer
  }

  boolean doSth(x) {
    if (!doer.validate(x)) throw new RuntimeException()
    true
  }
}

Spock 规范:

package de.scrum_master.Whosebug.q60341734

import org.junit.Rule
import org.junit.rules.TestName
import spock.lang.Specification

class SomethingTest extends Specification {
  @Rule
  TestName testName

  def "some test"() {
    given:
    def d = Mock(Doer)
    def s = new Something(d)

    when:
    s.doSth(2)

    then:
    1 * d.validate({ println "$testName.methodName: closure parameter = $it"; it == 2 }) >> true
  }

  def "another test"() {
    given:
    def d = Mock(Doer)
    def s = new Something(d)
    def myClosureVar = { println "$testName.methodName: closure parameter = $it"; it == 2 }

    when:
    s.doSth(2)

    then:
    1 * d.validate({ myClosureVar(it) }) >> true
  }
}

控制台日志:

some test: closure parameter = 2
another test: closure parameter = 2