在 Spock 中模拟 Kotlin 高阶函数

问题描述 投票:0回答:1

无法弄清楚如何使用 spock 模拟高阶函数。示例代码片段:

import jakarta.inject.Singleton

@Singleton
class SomeClass {
  fun bar(function: () -> Unit) {
    function()
  }
}

@Singleton
class SomeOtherClass {
  fun foo() {
    println("foo")
  }
}

@Singleton
class ClassUnderTest(
  private val someClass: SomeClass,
  private val someOtherClass: SomeOtherClass,
) {
  fun test() {
    // someOtherClass::foo type is KFunction0<Unit>
    someClass.bar(someOtherClass::foo)
  }
}

史波克测试:

class ClassUnderTestSpecification extends Specification {

  def someClass = Mock(SomeClass)
  def someOtherClass = Mock(SomeOtherClass)
  def classUnderTest = new ClassUnderTest(someClass, someOtherClass)

  def 'test class'() {
    when:
    classUnderTest.test()

    then:
    // someOtherClass::foo type is Groovy Closure
    // fails, as it should be the Kotlin KFunction
    1 * someClass.bar(someOtherClass::foo)
    0 * _
  }
}

正如代码片段中的几条注释所述,

someOtherClass::foo
在 Kotlin 代码 (KFunction) 和 Groovy/Spock (Groovy Closure) 之间返回不同。无论如何,我还没有找到获取实际的 KFunction 进行模拟的方法,它实际上应该只是对函数的引用,所以我觉得模拟不应该那么困难,我只是在这里遗漏了一些东西。

曾尝试将 Groovy 闭包转换为 KFunction,但没有成功(没想到它会起作用),尝试仅使用普通的

SomeOtherClass::foo
而不是特定的模拟实例,但仍然是 Groovy 闭包,等等。所有路径都有引导至:

Too many invocations for:

0 * _   (1 invocation)

Matching invocations (ordered by last occurrence):

1 * someClass.bar(fun com.example.package.SomeOtherClass.foo(): kotlin.Unit)   <-- this triggered the error
kotlin groovy mocking spock higher-order-functions
1个回答
0
投票

我不是 Kotlin 用户。起初,我想知道为什么我的错误消息与你的不同:

1 * someClass.bar(function foo (Kotlin reflection is not available))   <-- this triggered the error

原因解释如下这里:我必须添加

kotlin-reflect
作为我的示例 Kotlin 模块的依赖项。

不幸的是,Groovy 中似乎没有很好的方法来使用 Kotlin 函数引用。所以我们能做的就是

  • 验证类型
    Function0<Unit>
  • 使用参数约束闭包,如here所述,并尽可能匹配其
    toString()
    输出。

这是一个与不带

kotlin-reflect
的变体相匹配的变体:

import kotlin.Unit
import kotlin.jvm.functions.Function0
import spock.lang.Specification

class KotlinHigherOrderFunctionTest extends Specification {
  def someClass = Mock(SomeClass)
  def someOtherClass = Mock(SomeOtherClass)
  def classUnderTest = new ClassUnderTest(someClass, someOtherClass)

  def 'test class'() {
    when:
    classUnderTest.test()

    then:
    1 * someClass.bar(
      { it =~ /function foo |fun .*\.SomeOtherClass\.foo\(\): kotlin\.Unit/ } as Function0<Unit>
    )
    0 * _
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.