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

JUnit 5 @Testcontainers在Cucumber与Spring Boot 3中失效问题排查

Cucumber环境中@Testcontainers注解失效的原因分析

在编写Cucumber测试时,发现@Testcontainers注解无法正常工作。使用以下配置时:

import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.containers.OracleContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Testcontainers
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@ActiveProfiles("h2")
public class CucumberSpringConfiguration {
    @Container
    @ServiceConnection
    static OracleContainer oracleContainer = new OracleContainer("gvenzl/oracle-xe:21-slim-faststart");

    @Container
    @ServiceConnection
    static KafkaContainer kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.2.1"));
}

Spring应用启动失败,报错信息如下:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Mapped port can only be obtained after the container is started
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644)
    ... 76 more
Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
    at org.testcontainers.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:512)
    at org.testcontainers.containers.ContainerState.getMappedPort(ContainerState.java:161)
    at org.testcontainers.containers.OracleContainer.getOraclePort(OracleContainer.java:194)
    at org.testcontainers.containers.OracleContainer.getJdbcUrl(OracleContainer.java:120)
    at org.springframework.boot.testcontainers.service.connection.jdbc.JdbcContainerConnectionDetailsFactory$JdbcContainerConnectionDetails.getJdbcUrl(JdbcContainerConnectionDetailsFactory.java:65)
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.createDataSource(DataSourceConfiguration.java:56)
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari.dataSource(DataSourceConfiguration.java:117)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140)
    ... 77 more

核心问题是容器未启动,@Testcontainers注解未生效,JUnit的@BeforeAll/@AfterAll也未被调用。

改用Cucumber的@BeforeAll/@AfterAll手动启动容器后,测试可正常运行:

import io.cucumber.java.AfterAll;
import io.cucumber.java.BeforeAll;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.KafkaContainer;
import org.testcontainers.containers.OracleContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@Testcontainers
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@ActiveProfiles("h2")
public class CucumberSpringConfiguration {
    @Container
    @ServiceConnection
    static OracleContainer oracleContainer = new OracleContainer("gvenzl/oracle-xe:21-slim-faststart");

    @Container
    @ServiceConnection
    static KafkaContainer kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:6.2.1"));

    @BeforeAll
    public static void setUp() {
        System.out.println("++++++++ BEFORE ALL ++++++++");

        oracleContainer
                .withReuse(true)
                .start();

        kafkaContainer
                .withReuse(true)
                .start();
    }

    @AfterAll
    public static void tearDown() {
        System.out.println("++++++++ AFTER ALL ++++++++");

        oracleContainer.stop();
        kafkaContainer.stop();
    }
}

环境依赖配置

Maven依赖管理与依赖项

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-bom</artifactId>
            <version>7.18.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.junit</groupId>
            <artifactId>junit-bom</artifactId>
            <version>5.10.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>


    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-suite</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-spring</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-junit-platform-engine</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-testcontainers</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>oracle-xe</artifactId>
        <version>1.19.8</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>kafka</artifactId>
        <version>1.19.8</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>1.19.8</version>
        <scope>test</scope>
    </dependency>

Cucumber运行器配置

import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;

import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;


@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example.cucumber")
public class CucumberRunnerTest {
}

问题原因

  1. 注解归属与引擎兼容性问题:@Testcontainers是JUnit Jupiter的扩展注解,仅由JUnit Jupiter测试引擎处理。当前测试使用的是Cucumber的JUnit Platform引擎,Cucumber不会识别并执行JUnit Jupiter的@Testcontainers、@BeforeAll、@AfterAll注解逻辑,导致容器无法自动启动。

  2. Spring上下文初始化时机冲突:Spring Boot启动时,@ServiceConnection会尝试获取容器的映射端口来配置数据源和Kafka连接,但此时容器未启动(因为@Testcontainers未生效),直接触发"Mapped port can only be obtained after the container is started"异常。

  3. Cucumber钩子的有效性:改用Cucumber的@BeforeAll/@AfterAll后,这些钩子会被Cucumber引擎在测试生命周期中执行,确保容器在Spring上下文初始化前完成启动,端口可用,因此应用能正常初始化并运行测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 03:14:53