Spring TestExecutionListener无法注入随机端口问题求助
问题原因分析
直接在AbstractTestExecutionListener子类中使用@LocalServerPort注入端口会失败,因为TestExecutionListener实例由测试框架直接实例化,不属于Spring容器管理的Bean,Spring不会对其执行依赖注入逻辑,导致端口值始终为默认的0,触发Port must be greater than 0异常。
解决方案
以下两种方案都能解决端口注入问题,同时实现消除测试重复代码的目标:
方案一:通过ApplicationContextAware获取端口
让IntegrationHelper实现ApplicationContextAware接口,从Spring环境变量中直接读取随机端口:
1. 修改IntegrationHelper类
public class IntegrationHelper extends AbstractTestExecutionListener implements ApplicationContextAware { private int localServerPort; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 从Spring环境变量中获取随机端口 Environment env = applicationContext.getEnvironment(); this.localServerPort = Integer.parseInt(env.getProperty("local.server.port")); } @Override public void beforeTestMethod(TestContext testContext) throws Exception { // 初始化RestAssured端口 RestAssured.port = localServerPort; } @Override public void afterTestMethod(TestContext testContext) throws Exception { // 清空指定数据库表逻辑 JdbcTemplate jdbcTemplate = testContext.getApplicationContext().getBean(JdbcTemplate.class); jdbcTemplate.execute("TRUNCATE TABLE target_table"); } }
2. 完善自定义@IntegrationTest注解
确保合并默认的TestExecutionListeners,避免覆盖SpringBoot的核心测试功能:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestExecutionListeners( listeners = IntegrationHelper.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS ) public @interface IntegrationTest { }
3. 测试类使用注解
@IntegrationTest public class SampleIntegrationTest { @Test void testApiCall() { // RestAssured已自动配置好随机端口 given().when().get("/api/test").then().statusCode(200); } }
方案二:将IntegrationHelper转为Spring Bean
把IntegrationHelper定义为Spring管理的Bean,通过TestContext获取Bean并调用方法:
1. 定义IntegrationHelper Bean
@Component public class IntegrationHelper { @LocalServerPort private int localServerPort; @Autowired private JdbcTemplate jdbcTemplate; public void setupRestAssuredPort() { RestAssured.port = localServerPort; } public void clearTargetTables() { jdbcTemplate.execute("TRUNCATE TABLE target_table"); } }
2. 自定义TestExecutionListener
public class IntegrationTestListener extends AbstractTestExecutionListener { @Override public void beforeTestMethod(TestContext testContext) throws Exception { IntegrationHelper helper = testContext.getApplicationContext().getBean(IntegrationHelper.class); helper.setupRestAssuredPort(); } @Override public void afterTestMethod(TestContext testContext) throws Exception { IntegrationHelper helper = testContext.getApplicationContext().getBean(IntegrationHelper.class); helper.clearTargetTables(); } }
3. 更新@IntegrationTest注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestExecutionListeners( listeners = IntegrationTestListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS ) public @interface IntegrationTest { }
关键注意点
- 必须设置
mergeMode = MERGE_WITH_DEFAULTS,否则会覆盖SpringBoot默认的TestExecutionListeners,导致@Autowired等核心注入功能失效; - 两种方案都通过自定义注解封装了重复配置,测试类只需添加
@IntegrationTest即可复用所有逻辑。
内容的提问来源于stack exchange,提问作者Jaeyoon Lee
相关产品推荐
相关产品推荐

