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

如何在Spock中Mock jOOQ查询结果?

问题描述

我正在开发Spring Boot应用,拥有如下Groovy类:

@Component
class MyClass {

    DSLContext jooq

    MyClass(DSLContext jooq) {
        this.jooq = jooq
    }

    void execute(String value) {
        Table tbl     // assume assigned a Table
        Field<?> fld  // assume assigned a Field

        List<Record> records = jooq.select(DSL.asterisk()).from(tbl).where(fld.eq(value)).fetch()

        InsertValueStepN<Record> insertStep = jooq.insertInto(tbl)?.columns()
        // steps to complete insertStep elided
        insertStep?.execute()
    }
}

同时拥有如下Spock单元测试类:

class MyClassTest extends Specification {

    DSLContext jooq = Mock()
    MyClass tested = new MyClass(jooq)

    def "execute doesn't throw exceptions when no records found"() {
        given:
            jooq.select(_ as Field[]) >> []
        expect:
            tested.execute(null)
    }
}

我的问题在于Mock jOOQ及jooq.select()的返回结果。需要让Spock通过jooq.select()的调用,但目前在调用from()时会出现空指针异常。也曾尝试使用jooq.select(DSL.asterisk()) >> [],但会抛出GroovyCastException,因为ArrayList类型的[]不是SelectSelectStep类型;而SelectSelectStep是接口,唯一实现类SelectImpl实例化难度大,且似乎并非正确方案。目前使用安全导航运算符(?.)让当前Mock通过测试,但这并非正确做法,希望能正确Mock jOOQ调用的结果,请问遗漏了什么?

解决方案

jOOQ的查询是链式调用,每个方法都返回不同的接口实例,不能直接返回List,必须逐层Mock每个步骤的返回值:

  1. 逐层Mock链式调用的每个环节
    jOOQ的select()返回SelectSelectStep,from()返回SelectFromStep,where()返回SelectConditionStep,最终fetch()才返回List<Record>。所以需要为每个步骤Mock对应的接口实例。

  2. 调整测试用例
    修改后的Spock测试代码如下:

    def "execute doesn't throw exceptions when no records found"() {
        given:
            // Mock 查询链的每个步骤
            SelectSelectStep selectStep = Mock()
            SelectFromStep fromStep = Mock()
            SelectConditionStep conditionStep = Mock()
            
            // 1. select() 返回对应的Mock对象
            jooq.select(_ as Field[]) >> selectStep
            // 2. from() 返回对应的Mock对象
            selectStep.from(_ as Table) >> fromStep
            // 3. where() 返回对应的Mock对象
            fromStep.where(_ as Condition) >> conditionStep
            // 4. fetch() 返回预期的空列表
            conditionStep.fetch() >> []
            
            // 同时处理insert的链式调用(避免空指针)
            InsertIntoStep insertIntoStep = Mock()
            InsertColumnsStep columnsStep = Mock()
            
            jooq.insertInto(_ as Table) >> insertIntoStep
            insertIntoStep.columns(_ as Field[]) >> columnsStep
            // 如果后续还有values()等操作,继续Mock对应的返回,最后execute()可以返回任意预期值
            columnsStep.values(_) >> Mock(InsertValueStepN)
            
        expect:
            tested.execute(null)
    }
    
  3. 核心要点

    • 不需要实例化jOOQ的内部实现类(如SelectImpl),Spock可以直接Mock接口类型,这是最简洁的方式。
    • 链式调用的每一步都需要Mock返回对象,否则会出现空指针异常。
    • 如果测试不关注insert逻辑的具体执行结果,只需Mock到能让代码正常走完的步骤即可,无需完全模拟真实执行流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 09:55:09