Spock中Mock的属性值无法被Kotlin代码读取该如何解决?
问题诱因
- GroovyMock的拦截逻辑仅对Groovy动态调用生效:GroovyMock基于Groovy动态元编程实现,仅能拦截Groovy代码侧的方法调用。Kotlin代码是静态编译的,调用类方法时走JVM静态分派逻辑,不会触发Groovy的动态拦截,会直接读取类实例的字段值,你没有给Mock实例的
prop字段赋值,所以读取结果为null。 - Kotlin类成员默认是final修饰:你的
TestEntity虽然标记了open,但内部的prop属性没有额外加open修饰,其对应的getProp()、setProp()方法默认是final的,常规Spock Mock(基于ByteBuddy实现)无法Stub final方法,这也是你替换为普通Mock后Groovy侧也读不到值的原因。
解决方案
方案1:直接使用真实实体实例(最推荐)
对于无复杂逻辑的简单实体类,不需要Mock,直接构造真实实例传入即可,无任何兼容问题:
given: def cut = new AClassUnderTest() def testEntity = new TestEntity("abc")
方案2:修改Kotlin代码允许方法重写,使用普通Spock Mock
给prop属性添加open修饰,允许getter方法被重写:
open class TestEntity(open var prop: String) { }
测试代码使用普通Mock而非GroovyMock:
def testEntityMock = Mock(TestEntity) testEntityMock.getProp() >> "abc" // 也可以直接写 testEntityMock.prop >> "abc" 效果一致
方案3:无需修改生产代码,直接给Mock实例的字段赋值
Groovy支持直接访问类的私有字段,你可以直接给prop字段赋值,这样不管是Kotlin还是Groovy侧读取都能拿到值:
given: def testEntityMock = GroovyMock(TestEntity) testEntityMock.@prop = "abc" // 直接给实例的prop字段赋值,不走getter/setter
方案4:开启Spock Final方法Mock能力
如果你使用Spock 2.0+版本,可以开启Final类/方法Mock能力,不需要修改生产代码的open修饰:
- 在测试资源路径下新建
META-INF/services/org.spockframework.mock.IMockFactory文件,写入以下内容:
org.spockframework.mock.bytebuddy.ByteBuddyMockFactory
- 测试代码使用普通
Mock即可Stub final方法:
def testEntityMock = Mock(TestEntity) testEntityMock.prop >> "abc"
内容的提问来源于stack exchange,提问作者Brendan Hill
相关产品推荐
相关产品推荐

