如何在Spock中Mock继承抽象类的自定义类?
能否在Spock中Mock继承抽象类的自定义类?
问题背景
你自定义了一个继承自org.modelmapper.AbstractConverter的转换器类:
public class MyConverter extends AbstractConverter<A, B> { @Override public B convert(A source) { // 业务实现 } }
其中父类AbstractConverter的声明是:
public abstract class AbstractConverter<S, D> implements Converter<S, D> { ... }
但在Spock测试中尝试Mock这个具体类时,抛出了IllegalArgumentException,异常栈指向CGLIB的桥接方法解析逻辑:
class ATestOverThere extends Specification { def myConverter = Mock(MyConverter) // 此处抛出异常 ... }
异常详情:
java.lang.IllegalArgumentException at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61) at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911) at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317) at org.spockframework.mock.runtime.ProxyBasedMockFactory$CglibMockFactory.createMock(ProxyBasedMockFactory.java:154) at org.spockframework.mock.runtime.ProxyBasedMockFactory.create(ProxyBasedMockFactory.java:68) at org.spockframework.mock.runtime.JavaMockFactory.createInternal(JavaMockFactory.java:59) at org.spockframework.mock.runtime.JavaMockFactory.create(JavaMockFactory.java:40) at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:44) at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:51) at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:296) at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:286) at org.spockframework.lang.SpecInternals.MockImpl(SpecInternals.java:105) at com.tuenti.services.argentina.business.products.sva.SvaManagerSpec.$spock_initializeFields(SvaManagerSpec.groovy:14)
答案:当然可以,但需要避开泛型桥接方法的坑
Spock完全支持Mock继承抽象类的类,你遇到的问题是CGLIB(Spock默认的字节码生成库)在处理泛型桥接方法时的兼容性问题:
AbstractConverter实现了泛型接口Converter<S,D>,Java编译时会自动生成桥接方法来处理泛型类型擦除;- 当你的
MyConverter只重写了带具体泛型参数的convert(A source)时,CGLIB生成代理类时无法正确解析自动生成的桥接方法,导致异常。
下面是几种可行的解决方案:
方案1:优先Mock接口而非具体类
既然MyConverter最终实现了Converter<A,B>接口,直接Mock这个接口是最简单的方式,还能符合面向接口编程的最佳实践:
// 方式1:显式指定类型 Converter<A, B> myConverter = Mock() // 方式2:利用Groovy的类型推断 def myConverter = Mock(Converter) as Converter<A, B>
这种方式完全避开了CGLIB处理抽象类泛型的问题,是最推荐的方案。
方案2:显式重写桥接方法(必须Mock具体类时)
如果业务场景要求必须MockMyConverter具体类,可以在自定义类中显式重写接口的泛型方法(Java会自动生成,但显式声明能帮CGLIB正确识别):
public class MyConverter extends AbstractConverter<A, B> { @Override public B convert(A source) { // 业务实现 } // 显式重写接口的桥接方法,帮助CGLIB解析 @Override public Object convert(Object source) { return convert((A) source); } }
这样修改后,CGLIB生成代理时就能正确处理桥接方法,不会再抛出异常。
方案3:升级Spock或依赖库版本
旧版本的Spock依赖的CGLIB存在泛型桥接方法处理的bug,升级到较新的版本可以解决问题:
- 如果使用Spock 1.x:尝试升级CGLIB到最新的兼容版本;
- 如果可以升级到Spock 2.x+:它默认使用ByteBuddy代替CGLIB,对泛型和桥接方法的处理更友好。
内容的提问来源于stack exchange,提问作者Manu Artero
相关产品推荐
相关产品推荐

