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

Spock框架Stubbing未按预期工作:存根方法返回null而非模拟响应

Spock框架Stubbing未按预期工作:存根方法返回null而非模拟响应

我来帮你排查这个问题,你的测试代码里有几个关键细节没处理对,导致存根没生效,咱们一步步来解决:

问题根源分析

  1. 存根位置错误:你把存根逻辑(>> mockResponse)放在了then块里,但Spock的执行顺序是given → when → then,当when块执行serviceA.methodUnderTest时,then块的存根还没生效,所以repositoryA.someMethod会返回默认的null。
  2. 测试参数未定义:你的测试方法里直接用了arg1和arg2,但这些变量既没赋值也没通过where块提供数据,参数不匹配的话,Spock找不到对应的存根,同样会返回null。
  3. @Shared注解的不合理使用:mock对象用@Shared会在多个测试方法间复用状态,容易导致测试隔离性问题,除非你明确需要保留状态,否则完全没必要。

修正后的测试代码

class ServiceATest extends Specification {
    // 去掉@Shared,每个测试方法创建独立的mock实例,保证测试隔离
    RepositoryA repositoryA = Mock(RepositoryA)
    ServiceB serviceB = Mock(ServiceB)
    ServiceA serviceA

    def "test methodUnderTest when repository returns non-null"() {
        given:
        // 初始化被测服务
        serviceA = new ServiceA(repositoryA, serviceB)
        // 明确测试参数
        String testArg1 = "demoArg1"
        String testArg2 = "demoArg2"
        // 构建模拟响应
        ObjectA mockResponse = ObjectA.builder()
                .field1("demo")
                .field2("demo")
                .field3(Instant.now().plus(3, ChronoUnit.DAYS))
                .build()
        
        // 提前设置存根,确保调用被测方法前生效
        1 * repositoryA.someMethod(testArg1, testArg2) >> mockResponse

        when:
        serviceA.methodUnderTest(testArg1, testArg2)

        then:
        // then块仅做交互验证(可选),比如验证后续逻辑的行为
        // 示例:如果else分支不会调用serviceB,可以加这个验证
        0 * serviceB._
    }
}

额外注意事项

如果需要匹配任意参数(比如测试不同参数组合),可以用Spock的参数匹配器:

// 匹配任意两个String类型参数
1 * repositoryA.someMethod(_ as String, _ as String) >> mockResponse

// 精确匹配特定值
1 * repositoryA.someMethod(eq("demoArg1"), eq("demoArg2")) >> mockResponse

另外,确保你的ObjectA.Builder能正确构建非null对象,避免因为Builder本身的问题导致模拟响应异常。

备注:内容来源于stack exchange,提问作者Ravish Khatri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 06:23:05