You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java多版本兼容测试:Java 17中Mockito.spy Graphics2D的非法访问问题及跨版本解决方案咨询

解决Java多版本兼容下Mockito Spy Graphics2D的测试问题

针对你遇到的Java 8/11/17多环境测试中Mockito Spy SunGraphics2D的冲突问题,这里有几个实用的解决方案,不需要在CI环境中手动切换命令参数:

方案一:通过Maven Surefire插件条件化配置JVM参数

这是最直接的解决方式,利用Maven的Profile或内置条件判断,让Surefire插件仅在Java 17及以上版本添加--add-opens参数。

方式1:使用Maven三元表达式(Maven 3.2.1+支持)

在你的pom.xml中配置Surefire插件,通过java.version属性判断版本:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>3.1.2</version> <!-- 建议使用最新稳定版,确保兼容性 -->
      <configuration>
        <argLine>
          ${java.version>=17? '--add-opens=java.desktop/sun.java2d=ALL-UNNAMED' : ''}
        </argLine>
      </configuration>
    </plugin>
  </plugins>
</build>

这样当Java版本≥17时,自动添加所需参数;Java 8/11时参数为空,不会触发JVM启动错误。

方式2:使用Maven Profile自动激活

如果你的Maven版本较低不支持三元表达式,可以用Profile来实现:

<profiles>
  <profile>
    <id>java-17-plus</id>
    <activation>
      <jdk>[17,)</jdk> <!-- Java 17及以上自动激活 -->
    </activation>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <configuration>
            <argLine>--add-opens=java.desktop/sun.java2d=ALL-UNNAMED</argLine>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

Profile会根据当前JDK版本自动激活,完全不需要手动干预。

方案二:自定义Graphics2D委托类,避开内部类依赖

如果不想依赖JVM参数配置,可以通过包装类绕过对SunGraphics2D的直接Spy。我们可以写一个简单的委托类,把真实的Graphics2D实例包装进去,然后Spy这个自定义类:

1. 实现委托类

public class DelegatingGraphics2D extends Graphics2D {
    private final Graphics2D delegate;

    public DelegatingGraphics2D(Graphics2D delegate) {
        this.delegate = delegate;
    }

    // 重写业务代码中需要用到的方法,全部委托给原始实例
    @Override
    public Color getColor() {
        return delegate.getColor();
    }

    // 根据你的测试需求,添加其他需要的方法,比如drawString、fillRect等
    @Override
    public void drawString(String str, int x, int y) {
        delegate.drawString(str, x, y);
    }

    // 注:如果需要重写的方法太多,可以考虑用动态代理,但测试场景下只需要覆盖用到的方法即可
}

2. 修改测试代码

@Test
public void testGraphics() throws Exception {
    BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
    Graphics2D originalGraphics = bi.createGraphics();
    // Spy自定义的委托类,而非原始的SunGraphics2D
    DelegatingGraphics2D graphics = Mockito.spy(new DelegatingGraphics2D(originalGraphics));
    
    // 业务测试逻辑不变
    graphics.getColor();
    Mockito.verify(graphics, times(1)).getColor();
}

这个方案彻底避开了对Sun内部类的反射访问,所有Java版本都能正常运行,不需要任何JVM参数。

方案三:升级Mockito及依赖(辅助优化)

虽然你当前用的Mockito 4.6.1已经支持Java 17,但确保依赖的ByteBuddy版本是最新的(Mockito 4.x对应ByteBuddy 1.12+),可以减少模块系统下的反射问题。不过这个方案通常需要配合方案一的参数配置才能彻底解决问题。

总结

  • 如果不想修改测试代码,优先选方案一,通过Maven配置自动适配不同Java版本;
  • 如果想彻底摆脱对内部API的依赖,推荐方案二,代码层面解决兼容性问题,更稳定。

内容的提问来源于stack exchange,提问作者gawi

火山引擎 最新活动