如何创建带有私有属性的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
相关产品推荐
相关产品推荐

