JDK17迁移后Spring+PowerMock测试套件加载ApplicationContext失败
JDK11迁移至JDK17后PowerMock+Spring测试套件执行失败问题
问题详情
应用从JDK11迁移到JDK17后,使用PowerMockRunner和PowerMockito.mockStaticMock静态final方法时,单测运行正常,但执行整个项目测试套件时失败。服务在Tomcat上运行无异常,仅JUnit测试抛出如下错误:
[[1;34INFO[m] 2024-01-17 16:45:27,194 ERROR TestContextManager Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener] to prepare test instance [com.project.FunctionalityTest@4cdb88d0] java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@51fcf350 testClass = com.project.FunctionalityTest, locations = [], classes = [com.project.spring.AppTestConfig], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = [], contextCustomizers = [], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.test.context.web.WebDelegatingSmartContextLoader, parent = null] at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:143) ... 47 common frames omitted Caused by: org.springframework.cglib.core.CodeGenerationException: java.lang.NoSuchMethodException-->com.cisco.config.esearch.spring.AppTestConfig$$SpringCGLIB$$0.CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;) ... 70 common frames omitted Caused by: java.lang.NoSuchMethodException: com.project.spring.AppTestConfig$$SpringCGLIB$$0.CGLIB$SET_THREAD_CALLBACKS([Lorg.springframework.cglib.proxy.Callback;) at java.base/java.lang.Class.getDeclaredMethod(Class.java:2675) ... 70 common frames omitted
相关配置
POM文件片段
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> ...(省略部分pom内容) </project>
测试类注解
@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { AppTestConfig.class }) @WebAppConfiguration @PowerMockIgnore({"org.apache.http.conn.ssl.*", "javax.net.ssl.*" , "javax.crypto.*", "javax.net.*", "org.bson.conversions.Bson","org.apache.http.*","com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"}) @PrepareForTest({StopClock.class, SLIMCacheManager.class, DBConnFactory.class, ResourceDAO.class, Bson.class, PropertyUtil.class, JSONProvider.class})
注:所有内容在JDK11下运行正常,完整Maven依赖树已提供。
解决方案
1. 升级PowerMock及Mockito版本
JDK17对字节码操作和反射的限制更严格,旧版PowerMock不兼容。需升级到支持JDK17的版本:
- 将
powermock-module-junit4、powermock-api-mockito2(或对应Mockito3/4版本的依赖)升级至2.0.9+(推荐最新稳定版) - 同步升级Mockito到与PowerMock兼容的版本(如Mockito 4.x+,需匹配PowerMock版本要求)
2. 扩展PowerMockIgnore配置
添加Spring CGLIB相关包到忽略列表,避免PowerMock篡改Spring生成的代理类:
@PowerMockIgnore({ // 原有配置 "org.springframework.cglib.*", "net.sf.cglib.*" })
3. 切换Spring代理方式为JDK动态代理
修改AppTestConfig,强制Spring使用JDK动态代理替代CGLIB,避免PowerMock干扰代理类生成:
@Configuration public class AppTestConfig { @Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor() { return beanFactory -> { ((DefaultListableBeanFactory) beanFactory).setProxyTargetClass(false); }; } }
或直接在测试类的@ContextConfiguration中添加属性:
@ContextConfiguration(classes = { AppTestConfig.class }, properties = "spring.aop.proxy-target-class=false")
4. 清理@PrepareForTest范围
确保@PrepareForTest仅包含需要Mock的目标类,不要将Spring配置类(如AppTestConfig)加入其中,避免PowerMock不必要地代理Spring上下文类。
5. 添加JVM适配参数
在Maven Surefire插件中添加JVM参数,放宽JDK17的反射访问限制:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> <configuration> <argLine> --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens org.springframework.cglib.core=ALL-UNNAMED --add-opens org.springframework.cglib.proxy=ALL-UNNAMED </argLine> </configuration> </plugin> </plugins> </build>
内容的提问来源于stack exchange,提问作者mohitkira
相关产品推荐
相关产品推荐

