使用PowerMock在Kotlin中模拟软件包级别的功能

jma*_*nso 7 unit-testing mockito powermock kotlin

我在Kotlin中有一个包含某些程序包级功能的文件。

//Logger.kt

fun info(tag : String, message : String){
...
}

fun error{....}
Run Code Online (Sandbox Code Playgroud)

我正在测试调用此kotlin文件的函数的类的函数,并且我想模拟它们。我知道包级别的函数就像Java中的静态方法一样,因此我一直在考虑使用PowerMock。

//MyClass: Class that calls Logger.kt functions
class MyClass {

   fun myFunction(){
       info("TAG", "Hello world!")
   }

}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

Dav*_*uca 5

您可以使用一种变通方法来模拟 Kotlin 的顶级函数。

解释

@PrepareForTest 注释确实有 2 个参数来提供上下文(类),您想在其中模拟某些东西或在何处模拟将要使用的东西。

第一个参数是valueif type Class<?>[]:在这里你可以提供一个类数组。例如:

@PrepareForTest(Class1::class, Class2::class, Class3::class, QueryFactory::class)
Run Code Online (Sandbox Code Playgroud)

fullyQualifiedNamestype的第二个参数String[]:在这里您可以提供一个包含类的完全限定名称的数组。例如:

@PrepareForTest(Class1::class, fullyQualifiedNames = arrayOf("x.y.z.ClassName"))
Run Code Online (Sandbox Code Playgroud)

假设我们有一个名为“MyUtils.kt”的 Kotlin 文件,它只包含顶级函数。如您所知,您不能从 Kotlin 文件中引用 MyUtilsKt 类,但可以从 Java 中引用。这意味着生成了静态类(我还没有足够的知识向您提供有关此的更多详细信息)并且它具有完全限定的名称。

解决方案

这个解决方案并不完美。我在我们的代码库上实现了它,它似乎有效。肯定是可以改进的。

  1. 我创建了一个名为 Kotlin 的文件TopLevelFunctionClass.kt,我在其中添加了仅包含顶级函数的“类”的完全限定名称。

internal const val MyUtilsKt = "com.x.y.z.MyUtilsKt"

不幸的是,我不得不对名称进行硬编码,因为注释参数必须是编译时常量。

  1. 我更新了@PrepareForTest我的测试类的注释如下:

    @RunWith(PowerMockRunner::class)
    @PrepareForTest(Class1::class, Class2::class, Class4::class,
    fullyQualifiedNames = [MyUtilsKt]) // the string constant declared in TopLevelFunctionClass.kt
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我更新了测试方法如下:

    中的顶级函数MyUtils.kt

    internal fun testMock(): Int {
        return 4
    }
    
    Run Code Online (Sandbox Code Playgroud)

    测试方法:

    @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.
    }
    
    Run Code Online (Sandbox Code Playgroud)

副作用:如果您重命名包含顶级函数的 kotlin 文件,您还必须更改TopLevelFunctionClass.kt. 一个可能的解决方案,以避免重命名问题补充:@file:JvmName("The name you want for this file")。如果您有 2 个同名文件,则会出现重复的 JVM 类名错误。


Phi*_*ann 3

您可以使用 PowerMock 来实现此目的。正如您已经指出的,Kotlin 为文件中的顶级函数生成一个静态 Java 类Logger.kt,名为LoggerKt.java. @file:JvmName(\xe2\x80\x9c...\xe2\x80\x9c)如果您愿意,可以通过用 注释文件来更改它。因此你可以这样做:

\n\n
@RunWith(PowerMockRunner.class)\n@PrepareForTest(LoggerKt.class)\npublic class MyClassTest {\n\n    @Test\n    public void testDoIt() {\n        PowerMockito.mockStatic(LoggerKt.class);\n\n        MyClass sut = new MyClass();\n        sut.myFunction(); //the call to info(...) is mocked.\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我试图让它在 Kotlin 中工作,但我没有找到一种方法来使 Kotlin 生成的LoggerJava 类可用作类文字,以便能够将其用于注释@PrepareForTest。虽然可以在 Kotlin 中引用生成的 Java 类

\n