我在Kotlin中有一些包含一些包级函数的文件。
//Logger.kt
fun info(tag : String, message : String){
...
}
fun error{....}
我正在测试调用此kotlin文件函数的类的函数,我想嘲笑它们。我知道包级函数就像Java中的静态方法一样,所以我一直在考虑使用PowerMock。
//MyClass: Class that calls Logger.kt functions
class MyClass {
fun myFunction(){
info("TAG", "Hello world!")
}
}
有什么想法吗?
答案 0 :(得分:2)
您可以使用PowerMock。正如您已经指出的那样,Kotlin为文件Logger.kt
中的顶级函数生成一个静态Java类,名为LoggerKt.java
。如果您愿意,可以通过使用@file:JvmName(“...“)
注释文件来更改它。因此你可以这样做:
@RunWith(PowerMockRunner.class)
@PrepareForTest(LoggerKt.class)
public class MyClassTest {
@Test
public void testDoIt() {
PowerMockito.mockStatic(LoggerKt.class);
MyClass sut = new MyClass();
sut.myFunction(); //the call to info(...) is mocked.
}
}
我试图让它在Kotlin中运行,但是我没有找到一种方法可以将Kotlin生成的Logger
Java类作为类文字提供,以便能够将它用于{{ 1}}注释。虽然在Kotlin中有可能to reference the generated Java class。
答案 1 :(得分:1)
有一种解决方法,可以用来模拟Kotlin的顶级功能。
说明
@PrepareForTest
注释确实有2个参数来提供上下文(类),您可以在其中模拟对象或在要使用的东西中进行模拟。
如果类型为value
,则第一个参数为Class<?>[]
:在这里您可以提供一组类。例如:
@PrepareForTest(Class1::class, Class2::class, Class3::class, QueryFactory::class)
类型为fullyQualifiedNames
的第二个参数String[]
:在这里您可以为数组提供类的完全限定名称。例如:
@PrepareForTest(Class1::class, fullyQualifiedNames = arrayOf("x.y.z.ClassName"))
比方说,我们有一个名为“ MyUtils.kt”的Kotlin文件,其中仅包含顶级功能。如您所知,您不能从Kotlin文件中引用MyUtilsKt类,但是可以从Java中引用。这意味着将生成静态类(我尚不具备足够的知识来为您提供有关此的更多详细信息),并且它具有完全限定的名称。
解决方案
此解决方案并不完美。我在我们的代码库中实现了它,并且似乎可以正常工作。当然可以改进。
TopLevelFunctionClass.kt
的Kotlin文件,在其中添加了仅包含顶层函数的“类”的完全限定名称。 internal const val MyUtilsKt = "com.x.y.z.MyUtilsKt"
不幸的是,我必须对名称进行硬编码,因为注释参数必须是编译时常量。
我更新了测试类的@PrepareForTest
批注,如下所示:
@RunWith(PowerMockRunner::class)
@PrepareForTest(Class1::class, Class2::class, Class4::class,
fullyQualifiedNames = [MyUtilsKt]) // the string constant declared in TopLevelFunctionClass.kt
我更新了以下测试方法:
MyUtils.kt
中的顶级功能:
internal fun testMock(): Int {
return 4
}
测试方法:
@Test
fun myTestMethod() {
...
mockStatic(Class.forName(MyUtilsKt)) // the string constant declared in TopLevelFunctionClass.kt
`when`(testMock()).thenReturn(10)
assertEquals(10, testMock()) // the test will successfully pass.
}
副作用:如果要重命名包含顶级功能的kotlin文件,则还必须更改TopLevelFunctionClass.kt
中定义的常数。避免重命名问题的可能解决方案是添加:@file:JvmName("The name you want for this file")
。如果您有两个具有相同名称的文件,则会收到重复的JVM类名称错误。