首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >元类构造函数覆盖@CompileStatic注释类中的方法不起作用

元类构造函数覆盖@CompileStatic注释类中的方法不起作用
EN

Stack Overflow用户
提问于 2019-12-25 07:10:41
回答 2查看 414关注 0票数 2

当我们对使用someClass.metaClass.constructor注释的类的方法中可用的任何特定类(如RESTClient)使用@CompileStatic时,构造函数重写根本不起作用。

当我们移除@CompileStatic注释时,它正常工作。我是不是遗漏了什么?

样本代码:

代码语言:javascript
运行
复制
@CompileStatic
class FooClass {

    String getDataFromProvider() {
        String url = "https://www.example.com"
        RESTClient restClient = new RESTClient(url)

        HttpResponseDecorator response = restClient.post([:]) as HttpResponseDecorator
        return response
    }
}

而测试用例:

代码语言:javascript
运行
复制
import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.RESTClient
import spock.lang.Specification

class FooContentSpec extends Specification {

    void "test getDataFromProvider method"() {
        given: "Rest url"
        String restURL = "https://www.example.com"

        and: "Mock RESTClient"
        RESTClient mockedRestClient = Mock(RESTClient)

        // THIS IS NOT WORKING
        RESTClient.metaClass.constructor = { Object url ->
            assert restURL == url
            return mockedRestClient
        }

        mockedRestClient.metaClass.post = { Map<String, ?> args ->
            return ""
        }

        when: "We hit the method"
        HttpResponseDecorator response = Content.getDataFromProvider()

        then: "We should get status 200"
        response.statusCode == 200
    }
}

根据花式郎文档:

MockForStubFor不能用于测试静态编译的类,例如用于使用@CompileStatic的Java类或Groovy类。要对这些类进行存根和/或模拟,可以使用Spock或Java模拟库之一。

预期行为

在这个场景中,RESTClient的构造函数重写应该在我们的测试用例中工作,因为我们不想在每个测试用例中使用第三方API。

实际行为

不幸的是,RESTClient没有被嘲弄,因为它每次都会碰到@CompileStatic注释。

环境信息

代码语言:javascript
运行
复制
------------------------------------------------------------
Gradle 3.5
------------------------------------------------------------

Groovy:       2.4.10,
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015,
JVM:          1.8.0_221 (Oracle Corporation 25.221-b11),
OS:           Mac OS X 10.15.2 x86_64

Jira:https://issues.apache.org/jira/browse/GROOVY-9353

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-12-25 19:24:42

您是对的,@CompileStatic不能与元类操作结合使用。原因是,顾名思义,所有这些都是在编译时解析和绑定的,因此没有元类查找,因此无法覆盖它。

我建议查看IoC/dependency,这样您就可以将模拟注入到代码中。使用经典的单元素会使您的代码更难测试。

票数 2
EN

Stack Overflow用户

发布于 2019-12-27 05:15:36

伦纳德·布吕宁斯的评论之后

是的,@CompileStatic将在编译时解析RESTClient在您的FooClass中的构造函数,因此它不会在运行时使用元类来锁定它。如果您想看看它的外观,我建议使用反编译器,例如bytecode查看器,并查看生成的字节码。

我们将生成的字节码解压缩为两种情况:

@CompileStatic

代码语言:javascript
运行
复制
public class FooClass implements GroovyObject {
    public FooClass() {
        MetaClass var1 = this.$getStaticMetaClass();
        this.metaClass = var1;
    }

    public String getDataFromProvider() {
        String url = "https://www.example.com";

        // Directly constructor is getting used
        RESTClient restClient = new RESTClient(url);

        HttpResponseDecorator response = (HttpResponseDecorator)ScriptBytecodeAdapter.asType(restClient.post(ScriptBytecodeAdapter.createMap(new Object[0])), HttpResponseDecorator.class);
        return (String)ShortTypeHandling.castToString(response);
    }
}

@CompileStatic

代码语言:javascript
运行
复制
public class FooClass implements GroovyObject {
    public FooClass() {
        CallSite[] var1 = $getCallSiteArray();
        super();
        MetaClass var2 = this.$getStaticMetaClass();
        this.metaClass = var2;
    }

    public String getDataFromProvider() {
        CallSite[] var1 = $getCallSiteArray();
        String url = "https://www.example.com";

        // Here Groovy's metaprogramming is into play instead of directly calling constructor
        RESTClient restClient = (RESTClient)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(RESTClient.class, url), RESTClient.class);

        HttpResponseDecorator response = (HttpResponseDecorator)ScriptBytecodeAdapter.asType(var1[1].call(restClient, ScriptBytecodeAdapter.createMap(new Object[0])), HttpResponseDecorator.class);
        return (String)ShortTypeHandling.castToString(response);
    }
}

所以伦纳德的回答是完全正确的。我们忽略了这个简单的Java概念。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59476058

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档