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

如何创建带有私有属性的Spock Spy

如何创建带有私有属性的Spock Spy?

针对你的场景,问题出在FooServiceImpl没有暴露用于注入barService的构造方法或公共setter,而你尝试用constructorArgs传递参数的方式不适用(因为类本身没有对应的构造器)。下面是两种可行的实现方案:

方案1:利用Groovy特性直接访问私有字段

Groovy允许直接访问类的私有成员,这在Spock测试中非常方便,不需要额外的反射代码:

def "generate foo bar"() { 
    setup: 
    def barService = Mock(BarService) { 
        generateBar() >> "Bar" 
    } 
    // 创建FooServiceImpl的Spy实例
    FooServiceImpl spyFooService = Spy(FooServiceImpl)
    // 直接给私有属性barService赋值(Groovy支持绕过私有访问限制)
    spyFooService.barService = barService

    when:
    String result = spyFooService.generateFoo()

    then:
    // 验证结果是否符合预期
    result == "abcBar"
    // 验证barService的generateBar方法被调用了一次
    1 * barService.generateBar()
}

方案2:通过反射设置私有属性

如果你的环境限制了Groovy对私有字段的直接访问(比如启用了严格的安全管理器),可以用Java反射来手动设置私有属性:

def "generate foo bar with reflection"() { 
    setup: 
    def barService = Mock(BarService) { 
        generateBar() >> "Bar" 
    } 
    FooServiceImpl spyFooService = Spy(FooServiceImpl)
    
    // 通过反射获取私有字段并设置其可访问性
    Field barServiceField = FooServiceImpl.getDeclaredField("barService")
    barServiceField.setAccessible(true)
    barServiceField.set(spyFooService, barService)

    when:
    String result = spyFooService.generateFoo()

    then:
    result == "abcBar"
    1 * barService.generateBar()
}

额外说明

  • 你之前尝试的constructorArgs: [[barService: barService]]写法是错误的,因为FooServiceImpl并没有定义接收barService作为参数的构造函数,Spock无法通过这种方式完成依赖注入。
  • 如果你有权修改FooServiceImpl的代码,更优雅的方式是给它添加一个带参数的构造函数或者公共的setter方法,这样测试时注入依赖会更规范,不需要依赖Groovy特性或反射:
// 修改后的FooServiceImpl
public class FooServiceImpl { 
    private BarService barService; 

    // 添加带参数的构造函数
    public FooServiceImpl(BarService barService) {
        this.barService = barService;
    }

    // 或者添加公共setter
    public void setBarService(BarService barService) {
        this.barService = barService;
    }

    // 原有方法不变
    public String generateFoo() { 
        String barValue = barService.generateBar(); 
        return customFoo() + barValue; 
    } 
    public String customFoo() { 
        return "abc"; 
    } 
}

修改后,测试时就可以直接用constructorArgs来注入依赖了:

FooServiceImpl spyFooService = Spy(FooServiceImpl, constructorArgs: [barService])

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:34:01