You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MockK中Spyk模拟属性失效:调用getResult未返回预期对象

MockK spyk 属性模拟问题排查与解决

问题场景

在使用MockK测试Manager类时,已成功模拟myObject.ok属性,直接访问该属性的断言全部通过,但调用getResult()方法时返回的不是预期的mock实例,断言失败。

测试代码

val myObject = spyk<Manager>()

// 定义属性行为
val mockOK = mockk<Result>()
every { mockOK.id } returns "aaa"
every { mockOK.status } returns SessionStatus.OK
every { mockOK.timestamp } returns 123
every { mockOK.isValid() } returns true

every { myObject.ok } returns mockOK

// 使用模拟属性
val result = myObject.ok
assertEquals("aaa", result.id)  // 此断言通过
assertEquals(true, result.isValid())  // 此断言通过

val actual = myObject.getResult() 
assertEquals("aaa",actual.id) // 此断言失败

被测类代码

class Manager{
    var ok : Result = Result()
    var notOk: Result = Result()
    fun getResult() = if (ok.isValid()) { ok } else notOk
}

问题原因

问题出在spyk的属性mock方式和Kotlin编译器的内部优化:

  • 当你用every { myObject.ok } returns mockOK时,MockK仅拦截了外部调用的属性getter方法,所以测试代码中直接访问myObject.ok会返回mock实例。
  • 但Kotlin编译器对类内部的属性访问会做优化:直接访问属性的幕后字段(field),而非调用getter方法。因此getResult()内部访问ok时,拿到的是Manager实例初始化时的默认Result对象,而非你mock的mockOK。这就导致ok.isValid()返回默认值(false),最终getResult()返回notOk对象,断言失败。

解决方案

直接修改spyk实例的属性字段值,而非mock getter方法:

将测试代码中的:

every { myObject.ok } returns mockOK

替换为:

myObject.ok = mockOK

这样修改后,Manager实例的ok字段会被直接替换为mockOK,getResult()内部访问ok时拿到的就是mock实例,isValid()返回true,最终返回mockOK,断言即可通过。

另外,如果需要完全模拟Manager类而非使用部分模拟的spyk,也可以用mockk<Manager>(),并补充mockgetResult()的行为:

val myObject = mockk<Manager>()
// ... 定义mockOK的行为不变
every { myObject.ok } returns mockOK
every { myObject.getResult() } returns myObject.ok

内容的提问来源于stack exchange,提问作者bj4947

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:53:20