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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:22:35