如何使用私有 属性 创建 Spock Spy
How to create Spock Spy with private property
我关注Java class:
public class FooServiceImpl {
private BarService barService;
public String generateFoo() {
String barValue = barService.generateBar();
return customFoo() + barValue;
}
public String customFoo() {
return "abc";
}
}
这里是示例性的 Spock 测试方法:
def "generate foo bar"() {
setup:
def barService = Mock(BarService) {
generateBar() >> "Bar"
}
FooServiceImpl spyFooService =
Spy(FooServiceImpl, constructorArgs: [[barService: barService]])
spyFooService.customFoo() >> "foo"
when:
def fooValue = spyFooService.generateFoo()
then:
fooValue == "fooBar"
}
我尝试为 FooServiceImpl
class 创建 Spy 对象,但出现以下错误:
org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack:
No such property: barService for class:
com.foo.FooServiceImpl$$EnhancerByCGL`
我无法为 BarService
添加构造函数到 FooServiceImpl
或 setter,所以我想使用映射构造函数。这可能吗?
注意:根据 this issue 它应该有效
对于您的情况,最简单的解决方案是将此字段设置为 protected
而不是 private
。当您从 class 创建间谍对象时,会涉及 CGLIB,它会从您尝试从 class 创建间谍的 class 创建为子 class 在您的案例中.问题是您要修改的字段是私有字段,根据 Java 中的常规子 classing 策略,私有字段不会在子 class 中继承。这就是为什么间谍对象
中不存在字段 barService
ATTENTION: IntelliJ's debugger may tell you that barService
is present in this spyFromService
instance, however this is IDE's bug - if you list all available fields from spyFromService.class.fields
or spyFromService.class.declaredFields
you wont find barService
field here.
另一个问题是,当 CGLIB 参与对象创建过程时,它也会参与调用方法。这就是为什么通过 Groovy 的元编程功能向 class 或实例添加动态字段不起作用的原因。否则你将能够做这样的事情:
spyFromService.metaClass.barService = barService
或
spyFromService.class.metaClass.barService = barService
或者,您可以删除间谍对象并在测试中使用真实实例。然后
FooServiceImpl spyFromService = new FooServiceImpl()
spyFromService.@barService = barService
会起作用。但是,您将无法存根现有的 customFoo()
方法,您将不得不依赖于它的实际实现 returns.
我关注Java class:
public class FooServiceImpl {
private BarService barService;
public String generateFoo() {
String barValue = barService.generateBar();
return customFoo() + barValue;
}
public String customFoo() {
return "abc";
}
}
这里是示例性的 Spock 测试方法:
def "generate foo bar"() {
setup:
def barService = Mock(BarService) {
generateBar() >> "Bar"
}
FooServiceImpl spyFooService =
Spy(FooServiceImpl, constructorArgs: [[barService: barService]])
spyFooService.customFoo() >> "foo"
when:
def fooValue = spyFooService.generateFoo()
then:
fooValue == "fooBar"
}
我尝试为 FooServiceImpl
class 创建 Spy 对象,但出现以下错误:
org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack:
No such property: barService for class:
com.foo.FooServiceImpl$$EnhancerByCGL`
我无法为 BarService
添加构造函数到 FooServiceImpl
或 setter,所以我想使用映射构造函数。这可能吗?
注意:根据 this issue 它应该有效
对于您的情况,最简单的解决方案是将此字段设置为 protected
而不是 private
。当您从 class 创建间谍对象时,会涉及 CGLIB,它会从您尝试从 class 创建间谍的 class 创建为子 class 在您的案例中.问题是您要修改的字段是私有字段,根据 Java 中的常规子 classing 策略,私有字段不会在子 class 中继承。这就是为什么间谍对象
barService
ATTENTION: IntelliJ's debugger may tell you that
barService
is present in thisspyFromService
instance, however this is IDE's bug - if you list all available fields fromspyFromService.class.fields
orspyFromService.class.declaredFields
you wont findbarService
field here.
另一个问题是,当 CGLIB 参与对象创建过程时,它也会参与调用方法。这就是为什么通过 Groovy 的元编程功能向 class 或实例添加动态字段不起作用的原因。否则你将能够做这样的事情:
spyFromService.metaClass.barService = barService
或
spyFromService.class.metaClass.barService = barService
或者,您可以删除间谍对象并在测试中使用真实实例。然后
FooServiceImpl spyFromService = new FooServiceImpl()
spyFromService.@barService = barService
会起作用。但是,您将无法存根现有的 customFoo()
方法,您将不得不依赖于它的实际实现 returns.