你如何模拟 Kotlin 顶级函数?

How do you Mockk a Kotlin top level function?

Mockk 允许模拟静态函数,但如何模拟 Kotlin 顶级函数?

例如,如果我有一个名为 HelloWorld.kt 的 Kotlin 文件,我该如何模拟 sayHello() 函数?


HelloWorld.kt

fun sayHello() = "Hello Kotlin!"

基于@Sergey 的回答:

您可以在一个变量中实际实现 sayHello() 函数,然后该变量就是 sayHello().

函数参数的默认值

这个例子有效:

package tests

import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

val sayHelloKotlin = { "Hello Kotlin!" }
fun sayHello(producer: () -> String = sayHelloKotlin): String = producer()

class Tests {
    interface Producer {
        fun produce(): String
    }

    @Test
    fun `Top level mocking`() {
        val mock = mockk<Producer>()
        every { mock.produce() } returns "Hello Mockk"

        val actual = sayHello(mock::produce)
        Assertions.assertEquals(actual, "Hello Mockk")
    }
}

这样做的问题是,您更改生产代码只是为了满足测试需求,感觉做作。

有办法模拟顶级函数:

mockkStatic("pkg.FileKt")
every { fun() } returns 5

你只需要知道这个函数去哪个文件就可以了。签入 JAR 或堆栈跟踪。

要添加以前的答案,这是有效的:

mockkStatic("pkg.FileKt")
every { fun() } returns 5

其中 mockStatic 作为参数 "package_name:class_file_name" 但是为了简化 mockStatick 调用,您可以直接在文件中使用 @file:JvmName 为编译器命名文件。

HelloWorld.kt

@file:JvmName("hello")
fun sayHello() = "Hello Kotlin!"

HelloWorldTest.kt

mockkStatic("pkg.hello")
every { fun() } returns 5

关于为什么这是必要的更详细的解释和其他例子在这里:https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-advanced-features-42277e5983b5

以下语法对我有用。

mockkStatic(::sayHello.javaMethod!!.declaringClass.kotlin)

令我惊讶的是 jvm-stdlib 上还没有任何内容。

编辑: 现在官方已经引入了这个重载: https://github.com/mockk/mockk/pull/518

mockkStatic(::sayHello)

此代码不适用于 mockk 版本 1.10.0,但在 1.11.0 中运行良好(当然需要更改 mockkStatic(::bar) )

Utils.kt

@file:JvmName("UtilsKt")
package com.example.myapplication

fun foo(): Boolean {
  return bar()
}

fun bar():Boolean {
  return false
}

测试

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.O_MR1])
class ExampleUnitTest {
    @Test
    fun addition_isCorrect() {
        mockkStatic("com.example.myapplication.UtilsKt")
        every { bar() } returns true
        assertTrue(foo())
    }
}