You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:26:30