Java 如何使用私有财产创建Spock Spy

Java 如何使用私有财产创建Spock Spy,java,groovy,spock,Java,Groovy,Spock,我有以下Java类: public class FooServiceImpl { private BarService barService; public String generateFoo() { String barValue = barService.generateBar(); return customFoo() + barValue; } public String customFoo() { re

我有以下Java类:

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
类创建一个间谍对象,但出现以下错误:

org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack: 
No such property: barService for class: 
com.foo.FooServiceImpl$$EnhancerByCGL`
我无法将构造函数添加到
FooServiceImpl
BarService
的setter,因此我想使用map构造函数。这可能吗


注意:根据它应该起作用

在您的情况下,最简单的解决方案是将此字段
设置为受保护的
,而不是
私有的
。当您从类创建spy对象时,涉及CGLIB,它从您试图从中创建spy的类中创建子类-
com.foo.FooServiceImpl$$EnhancerByCGL
。问题是您试图修改的字段是私有字段,根据Java中的常规子类化策略,私有字段不会在子类中继承。这就是spy对象中不存在字段
barService
的原因

注意:IntelliJ的调试器可能会告诉您,
barService
存在于这个
spyFromService
实例中,然而,这是IDE的错误-如果您列出了
spyFromService.class.fields
spyFromService.class.declaredFields
中的所有可用字段,您将无法在此处找到
barService
字段

另一个问题是,当CGLIB参与对象创建过程时,如果涉及到调用方法,它也会参与。这就是为什么通过Groovy的元编程特性向类或实例添加动态字段是行不通的。否则,您将能够执行以下操作:

spyFromService.metaClass.barService = barService

或者,您可以去掉spy对象,并在测试中使用真实实例。然后

FooServiceImpl spyFromService = new FooServiceImpl()
spyFromService.@barService = barService
会有用的。但是,您将无法存根现有的
customFoo()
方法,您将不得不依赖其实际实现返回的内容

FooServiceImpl spyFromService = new FooServiceImpl()
spyFromService.@barService = barService