首页 文章

使用PowerMock在Kotlin中模拟包级功能

提问于
浏览
5

我在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!")
   }

}

有任何想法吗?

2 回答

  • 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类可用作类文字,以便能够将它用于 @PrepareForTest 注释 . 虽然有可能to reference the generated Java class在Kotlin .

  • 0

    您可以使用一种解决方法来模拟Kotlin的顶级功能 .

    Explanation

    @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引用 . 这意味着生成了静态类(我还没有足够的知识为您提供有关此内容的更多详细信息)并且它具有完全限定的名称 .

    Solution

    这个解决方案并不完美 . 我在我们的代码库上实现它,它似乎工作 . 当然可以改进 .

    • 我创建了一个名为 TopLevelFunctionClass.kt 的Kotlin文件,其中我添加了仅包含顶级函数的"classes"的完全限定名称 .

    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") . 如果你'll have 2 files with the same name you' ll得到重复JVM类名称错误 .

相关问题