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

Spock测试中Kotlin类的GroovySpy在Java类中失效问题

问题场景

用Groovy+Spock做单元测试时,设置reader.get(_ as Path, _ as String) >> fileA后,期望Java实现的Factory类调用reader.get()返回fileA,但实际执行了Kotlin Reader类的真实方法返回fileB。测试代码里直接调用factory.reader.get()能返回fileA,但Factory内部调用却不行。此前Mock Java对象正常,怀疑是Kotlin类导致该问题。

相关代码

测试代码(Groovy+Spock)

given:
def reader = GroovySpy(Reader)
def file = new File()
reader.get() >> file // 此处file对应fileA

and:
def factory = new Factory(reader)

when:
...

Kotlin实现的Reader类

class Reader(
    private val repoConfig: FactStoreRepoConfiguration = FactStoreRepoConfiguration()
) {
    fun get(): File {
        // 默认返回fileB的逻辑
    }
}

Java实现的Factory类

public final class Factory implements StoreFactory {

    private final Reader reader;

    public Store create() {
        System.out.println(reader.get()); // 期望输出fileA,实际输出fileB
        return new Store(reader);
    }
}
问题原因

Kotlin中默认所有类和方法都是final的,而Spock的GroovySpy默认只能mock非final方法。Java是静态编译语言,调用final方法时不会走Groovy的动态代理逻辑,直接执行原方法;而测试代码中用Groovy直接调用时,会通过动态派发触发mock逻辑,所以能返回预期值。

解决方法

有两种可行方案:

  1. 修改Kotlin类,将方法改为可继承
    在Reader类的get()方法前添加open关键字,允许方法被代理:

    class Reader(
        private val repoConfig: FactStoreRepoConfiguration = FactStoreRepoConfiguration()
    ) {
        open fun get(): File {
            // 默认返回fileB的逻辑
        }
    }
    
  2. 用Spock原生Mock()替代GroovySpy
    Spock的Mock()依赖Objenesis可以创建final类的代理,无需修改Kotlin源码:

    given:
    def reader = Mock(Reader)
    def file = new File()
    1 * reader.get() >> file // 明确指定调用次数与返回值
    
    and:
    def factory = new Factory(reader)
    
    when:
    factory.create()
    
    then:
    // 验证逻辑
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 17:12:15