问题描述
我很难使用反射在 Kotlin 中获取私有方法,以便将其作为参数传递给高阶函数,这是我得到的以及我需要做的:
inline fun <reified T> T.getPrivateFunc(name: String): KFunction<*> {
return T::class.declaredMemberFunctions.first {
it.name == name
}.apply {
isAccessible = true
}
}
这是我拥有的高阶函数:
class MyService {
fun myHigherOrderFunction(action: () -> Unit) { /*...*/ }
}
class SystemUnderTest {
fun privateFunc() { /*...*/ }
}
最后是一个单元测试,我试图确保将正确的方法传递给高阶函数,为了简化,我省略了细节:
// ...
val serviceMock = MyService()
val sut = SystemUndertest()
// Here is what I'm trying to accomplish
val privateMethod = sut.getPrivateMethod("privateFunc")
service.myHighOrderFunction(privateMethod)
// In the above line I get a compilation error: required () - Unit,found KFunction<*>
service.myHigherOrderFunction(privateMethod as () -> Unit)
// In the above line I get the following runtime error:
// ClassCastException: kotlin.reflect.jvm.internal.KFunctionImpl cannot be cast to kotlin.jvm.functions.Function1
我知道可以将 privateFunc
设为 public
并可能使用 @VisibleForTesting
对其进行注释来完成测试,但我想要的是尽可能避免损害设计。
有什么想法吗?提前致谢!
解决方法
我不认为 KFunction 和 KCallable 有任何绑定接收器的概念,因此它们不可调用(没有 operator fun invoke
),因此不符合函数的要求。所以我认为你必须将 KFunction 对象包装在一个函数中才能将它传递给你的高阶函数。要调用 KFunction,您需要传递接收者类的实例作为第一个参数。
val serviceMock = MyService()
val sut = SystemUnderTest()
val privateMethod = sut.getPrivateMethod("privateFunc")
service.myHighOrderFunction { privateMethod.call(sut) }
编辑:要内化包装函数的创建,您可以这样做:
inline fun <reified T> T.getZeroArgPrivateMethod(name: String): () -> Unit = {
T::class.declaredMemberFunctions.first {
it.name == name
}.apply {
isAccessible = true
}.call(this)
}
//...
val serviceMock = MyService()
val sut = SystemUnderTest()
val privateMethod = sut.getZeroArgPrivateMethod("privateFunc")
service.myHighOrderFunction(privateMethod)