元类构造函数重写不适用于@CompileStatic注释类中的方法

问题描述 投票:1回答:2

当我们在用someClass.metaClass.constructor注释的类的方法内将任何可用的特定类(例如RESTClient)使用RESTClient时,构造函数覆盖完全无效。

当我们删除@CompileStatic注释时,此方法正常工作。我想念什么吗?

示例代码:

@CompileStatic

和测试用例:

@CompileStatic
class FooClass {

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

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

根据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 } } 文档:

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

预期的行为

在这种情况下,@CompileStatic的构造函数重写应该在我们的测试用例中起作用,因为我们不想在每个测试用例中都使用第三方API。

实际行为

[不幸的是,RESTClient不会因为它每次都击中API的RESTClient注释而被嘲笑。

环境信息

@CompileStatic

吉拉:------------------------------------------------------------ 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

java unit-testing groovy metaprogramming spock
2个回答
2
投票

您是正确的https://issues.apache.org/jira/browse/GROOVY-9353不能与元类操作一起使用。原因是,顾名思义,所有这些都是在编译时解析和绑定的,因此没有元类查找,因此也没有办法覆盖它。

我建议研究IoC /依赖性注入,以便您可以将模拟注入到代码中。使用经典单例会使您的代码难以测试。


0
投票

[@CompileStatic的评论后:

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

我们针对两种情况反编译了生成的字节码:

带有Leonard Brünings

@CompileStatic

没有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

所以@Leonard给出的答案是完全正确的。我们错过了这个简单的Java概念。

© www.soinside.com 2019 - 2024. All rights reserved.