当我们对使用someClass.metaClass.constructor注释的类的方法中可用的任何特定类(例如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 Lang文档:


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


预期行为

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

实际行为

不幸的是,由于RESTClient注释每次都会命中API,因此不会嘲笑@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


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

最佳答案

没错,@CompileStatic不能与元类操作一起使用。顾名思义,原因是所有内容都在编译时解析和绑定,因此没有元类查找,因此也没有方法可以覆盖它。

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

09-30 18:42