无法弄清楚如何使用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进行mocking,它应该只是一个函数的引用,所以我觉得mocking应该不会那么难,我只是在这里缺少了一些东西。
尝试过将Groovy闭包转换为KFunction,但没有成功(没有期望它能工作),尝试过只使用普通的SomeOtherClass::foo
而不是特定的mock示例,但仍然是一个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
2条答案
按热度按时间xqkwcwgp1#
在某些情况下,可以放松参数并调用传递的函数来测试它们的行为,而不是类型/名称相等。
在您的例子中,传递给
bar
的lambda在其中被调用。例如,如果传递的函数调用了一些mock,你可以这样做:
rwqw0loc2#
我不是Kotlin用户。首先,我想知道为什么我的错误消息与你的不同:
原因解释为here:我必须将
kotlin-reflect
作为依赖项添加到示例Kotlin模块中。方案一:使用Kotlin反射来确定参数细节
不幸的是,在Groovy中似乎没有使用Kotlin函数引用的好方法。所以我们能做的就是
Function0<Unit>
类型,toString()
输出。下面是一个与不带
kotlin-reflect
和带kotlin-reflect
的变体相匹配的变体:或者,如果你更喜欢子串匹配而不是正则表达式匹配:
如果你想放松你的参数约束,只检查类型,你当然可以简单地使用:用途:
可能有更好、更精确的方法来解决这个问题,但是必须有更精通Groovy和Kotlin的人来回答这个问题。
方案二:使用Spock spy将方法调用传递给二级模拟,并在调用后者时进行验证
就像伦纳德已经在他的评论中说过的,你似乎真的以一种非常严格的方式过度指定了你的特性,使得规范在重构规范下的主题时变得脆弱。
无论如何,你可以使用一些我称之为模拟和间谍之间的混合体的东西,即。一个测试double包裹在一个真实的对象示例周围,但默认情况下返回模拟响应,而普通的spy将通过所有方法调用。然后,将您感兴趣的一个方法调用传递给 Package 的示例,在本例中, Package 的示例是另一个mock,您可以在其上验证方法调用。
当然,对于这个简单的设置,默认的响应+覆盖解决方案是不必要的,因为
SomeClass
只有一个方法。但是如果你的真实的类有多个方法,并且你真的想尽可能多地模拟它,因为它是一个测试依赖项而不是规范下的主题,上面是一种实现方法。