Spock测试Java代码时无法Mock HttpRequest构造器,Groovy代码却正常工作的原因咨询
我来给你拆解下这个问题的核心原因,其实本质是Java和Groovy的运行时构造器调用机制差异,加上Spock的GroovyMock的工作限制导致的:
GroovyMock的工作依赖Groovy元编程体系GroovyMock(包括你用的全局构造器Mock)是基于Groovy的元编程能力实现的。Groovy类在运行时所有的方法、构造器调用都会经过Groovy的元类(MetaClass)层,当你配置GroovyMock(HttpRequest, global: true)时,本质是替换了元类中构造器的逻辑,让new HttpRequest()返回你预先Mock好的实例。Java类的构造器调用是静态绑定的,绕过Groovy元编程拦截
你的Github类是Java编写的,它里面的new HttpRequest()会被Java编译器编译成invokespecial字节码指令——这个指令是直接、静态绑定到HttpRequest构造器的,完全不会经过Groovy的元编程拦截逻辑。所以即便你声明了全局GroovyMock,也根本拦截不到Java类里的构造器调用,自然会执行真实的HttpRequest构造逻辑,导致测试失败。Groovy类的构造器调用会经过元编程拦截点
当你把Github、HttpRequest改成Groovy类后,它们的构造器调用会被Groovy运行时自动重定向到元类的newInstance方法,这时候GroovyMock的全局拦截逻辑就能生效,成功替换构造器的返回值,所以测试能正常通过。ByteBuddy的角色说明
你依赖中的ByteBuddy是Spock底层用来做字节码增强的工具,但GroovyMock的全局构造器Mock并没有用ByteBuddy来拦截Java类的构造器调用——这种全局Mock方式是Groovy特有的,仅对Groovy代码生效。如果要Mock Java类的构造器,需要依赖更复杂的字节码增强方案,但Spock本身对Java类的全局构造器Mock支持有限,这也是为什么官方更推荐通过依赖注入重构架构(比如你提到的传入HttpRequest实例)的原因。
内容来源于stack exchange

