如何在 Kotlin 中模拟没有 class 的函数?
How to mock function without class in Kotlin?
fun add() {
return 4+1;
}
class Calculator {
fun MathUtils() {
// do something
// calls add() function
val x: Int = add()
// return something
return x + 22
}
}
class CalculatorTest {
var c = Calculator()
@Test
fun MathUtilsSuccess() {
Assertions.assertThat(
c.MathUtils()
).isEqualTo(24)
}
}
我是单元测试的新手,我想知道有什么方法可以调用 MathUtils()
函数(在计算器 class 内)
in MathUtilsSuccess()
(在 CalculatorTest class 内),我必须模拟 add()
函数,它不在任何 class 内,这样 add()
总是 returns 2,这样在成功场景中我的测试就通过了。
所有 classes 都在单独的文件中,fun add() 也在单独的文件中。
P.S : 我已经将我的疑问分解为这个简单的例子,这不是我正在处理的实际问题。
确实 io.mockk:mockk 支持在 Kotlin 中模拟 top-level 函数。
与 extension functions 类似,对于 top-level 函数,Kotlin 创建了一个 class 并在幕后包含静态函数,这些函数又可以被模拟。
但是,您需要知道所创建的基础 class 的名称,这暗示这种方法可能只应很少使用并谨慎使用。我将首先演示一个工作示例,然后命名一些替代方法。
让我们看一个关于如何使用 MockK
.
模拟 top-level 函数的例子
foo.kt
package tld.domain.example
fun foo(x: Int): Int = x + 3
bar.kt
package tld.domain.example
fun bar(z: Int): Int = foo(z) + 2
您可以模拟静态“事物”,例如 扩展函数 、top-level 函数 和 对象 使用 mockkStatic
.
internal class BarKtTest {
@Test
internal fun `can work without mock`() {
unmockkAll() // just to show nothing is mocked anymore
val result = bar(1)
assertThat(result, equalTo(6))
}
@Test
internal fun `can be mocked`() {
mockkStatic("tld.domain.example.FooKt")
every { foo(any()) } returns 1
val result = bar(1)
assertThat(result, equalTo(3))
}
}
如上所示,可以模拟 top-level 函数产生不同的结果。
上面的示例使用 com.natpryce:hamkrest 作为他们的断言,但这应该无关紧要。
使用 IntelliJ 时,您可以使用 Tools > Kotlin > Show Kotlin Bytecode
检索基础 class 的名称。在我上面的例子中,这会产生 a
public final class tld/domain/example/FooKt {
...
mockkStatic
有一个重载版本,允许提供函数引用而不是 hard-coding 包名和 class 名称作为字符串。但是请注意,这在幕后依赖于相同的方法。
mockkStatic(::foo)
除了使用静态模拟之外,您还可以利用依赖倒置原则,即将 foo
的实现以某种方式注入 bar
,例如通过它的参数或将其包装在包含字段或使用更高级别函数的 class 中。
fun barWithParam(foo: (Int) -> Int, z: Int): Int =
foo(z) + 2
class BarProvider(private val foo: (Int) -> Int) {
fun bar(z: Int): Int = foo(z) + 2
}
fun barFactory(foo: (Int) -> Int): (Int) -> Int {
return { z -> foo(z) + 2 }
}
val bar = barFactory(::foo)
另一种方法是简单地忽略 bar
在幕后使用 foo
并在不模拟 foo
的情况下测试 bar
的行为这一事实。这主要适用于 foo
是没有任何 side-effect 的纯函数,例如进行任何 I/O 操作,例如网络、磁盘...
fun add() {
return 4+1;
}
class Calculator {
fun MathUtils() {
// do something
// calls add() function
val x: Int = add()
// return something
return x + 22
}
}
class CalculatorTest {
var c = Calculator()
@Test
fun MathUtilsSuccess() {
Assertions.assertThat(
c.MathUtils()
).isEqualTo(24)
}
}
我是单元测试的新手,我想知道有什么方法可以调用 MathUtils()
函数(在计算器 class 内)
in MathUtilsSuccess()
(在 CalculatorTest class 内),我必须模拟 add()
函数,它不在任何 class 内,这样 add()
总是 returns 2,这样在成功场景中我的测试就通过了。
所有 classes 都在单独的文件中,fun add() 也在单独的文件中。
P.S : 我已经将我的疑问分解为这个简单的例子,这不是我正在处理的实际问题。
确实 io.mockk:mockk 支持在 Kotlin 中模拟 top-level 函数。
与 extension functions 类似,对于 top-level 函数,Kotlin 创建了一个 class 并在幕后包含静态函数,这些函数又可以被模拟。
但是,您需要知道所创建的基础 class 的名称,这暗示这种方法可能只应很少使用并谨慎使用。我将首先演示一个工作示例,然后命名一些替代方法。
让我们看一个关于如何使用 MockK
.
foo.kt
package tld.domain.example
fun foo(x: Int): Int = x + 3
bar.kt
package tld.domain.example
fun bar(z: Int): Int = foo(z) + 2
您可以模拟静态“事物”,例如 扩展函数 、top-level 函数 和 对象 使用 mockkStatic
.
internal class BarKtTest {
@Test
internal fun `can work without mock`() {
unmockkAll() // just to show nothing is mocked anymore
val result = bar(1)
assertThat(result, equalTo(6))
}
@Test
internal fun `can be mocked`() {
mockkStatic("tld.domain.example.FooKt")
every { foo(any()) } returns 1
val result = bar(1)
assertThat(result, equalTo(3))
}
}
如上所示,可以模拟 top-level 函数产生不同的结果。 上面的示例使用 com.natpryce:hamkrest 作为他们的断言,但这应该无关紧要。
使用 IntelliJ 时,您可以使用 Tools > Kotlin > Show Kotlin Bytecode
检索基础 class 的名称。在我上面的例子中,这会产生 a
public final class tld/domain/example/FooKt {
...
mockkStatic
有一个重载版本,允许提供函数引用而不是 hard-coding 包名和 class 名称作为字符串。但是请注意,这在幕后依赖于相同的方法。
mockkStatic(::foo)
除了使用静态模拟之外,您还可以利用依赖倒置原则,即将 foo
的实现以某种方式注入 bar
,例如通过它的参数或将其包装在包含字段或使用更高级别函数的 class 中。
fun barWithParam(foo: (Int) -> Int, z: Int): Int =
foo(z) + 2
class BarProvider(private val foo: (Int) -> Int) {
fun bar(z: Int): Int = foo(z) + 2
}
fun barFactory(foo: (Int) -> Int): (Int) -> Int {
return { z -> foo(z) + 2 }
}
val bar = barFactory(::foo)
另一种方法是简单地忽略 bar
在幕后使用 foo
并在不模拟 foo
的情况下测试 bar
的行为这一事实。这主要适用于 foo
是没有任何 side-effect 的纯函数,例如进行任何 I/O 操作,例如网络、磁盘...