简体   繁体   English

如何使用私有财产创建Spock间谍

[英]How to create Spock Spy with private property

I have following Java class: 我有以下Java类:

public class FooServiceImpl {
    private BarService barService;

    public String generateFoo() {
        String barValue = barService.generateBar();
        return customFoo() + barValue;
    }

    public String customFoo() {
       return "abc";
    }
}

And here is exemplary Spock test method: 这是示例性的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"
}

I try to create a Spy object for FooServiceImpl class but I get following error: 我尝试为FooServiceImpl类创建一个Spy对象,但出现以下错误:

org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack: 
No such property: barService for class: 
com.foo.FooServiceImpl$$EnhancerByCGL`

I can't add a constructor to FooServiceImpl or setter for BarService , so I want to use map constructor. 我不能添加一个构造函数来FooServiceImpl或setter方法BarService ,所以我想用地图构造函数。 Is this possible? 这可能吗?

Note: according to this issue it should work 注意:根据此问题,它应该可以工作

The easiest solution in your case would be to make this field protected instead of private . 在您的情况下,最简单的解决方案是使该字段protected而不是private When you create a spy object from a class, CGLIB is involved and it creates as subclass from the class you are trying to create spy from - com.foo.FooServiceImpl$$EnhancerByCGL in your case. 当您从类创建间谍对象时,会涉及CGLIB,它会从您要从中创建间谍的类的子类中创建com.foo.FooServiceImpl$$EnhancerByCGL The thing is that the field you are trying to modify is a private field and according to regular subclassing strategy in Java, private field does not get inherited in child class. 问题是您要修改的字段是一个私有字段,根据Java中的常规子类化策略,私有字段不会在子类中继承。 That is why field barService does not exist in spy object 这就是为什么在间谍对象中不存在barService字段的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. 注意:的IntelliJ的调试器可以告诉你, barService存在于这个spyFromService情况下,然而,这是IDE的错误-如果你列出所有可用字段spyFromService.class.fieldsspyFromService.class.declaredFields你不会找到barService此处字段。

Another problem is that when CGLIB gets involved in object creation process it also gets involved if it comes to invoking methods. 另一个问题是,当CGLIB参与对象创建过程时,如果涉及到调用方法,它也会也参与其中。 This is why adding dynamic fields to a class or instance via Groovy's metaprogramming features wont work. 这就是为什么无法通过Groovy的元编程功能将动态字段添加到类或实例中的原因。 Otherwise you would be able to do things like: 否则,您将可以执行以下操作:

spyFromService.metaClass.barService = barService

or 要么

spyFromService.class.metaClass.barService = barService

Alternatively you could get rid of spy object and use a real instance in your test. 或者,您可以摆脱间谍对象,并在测试中使用真实实例。 Then 然后

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

will work. 将工作。 However you won't be able to stub existing customFoo() method and you will have to rely on what its real implementation returns. 但是,您将无法对现有的customFoo()方法进行存根,并且必须依靠其实际实现返回的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM