Junit 5 的 @Testcontainers 不适用于黄瓜和 Spring Boot 3

问题描述 投票:0回答:1

我编写了一些黄瓜测试,并注意到 @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,它们根本不会被调用,因此 junit 集成对我来说似乎已经死了。

现在,如果我手动启动 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();
    }
}

请注意,我使用的是 cucumber 包中的 BeforeAll/AfterAll,而不是 junit。

在我在互联网上看到的所有教程中,@Testcontainer 似乎与黄瓜配合得很好。

我正在使用 Spring Boot 3.3.0 和以下 junit/cucumber 依赖项:

<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>

黄瓜配置类如下所示:

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 {
}

可能是什么问题?

spring-boot cucumber junit5 testcontainers-junit5 cucumber-spring
1个回答
0
投票

@Testcontainers 是 JUnit Jupiter 扩展

1.1。什么是 JUnit 5?

与以前版本的 JUnit 不同,JUnit 5 由来自三个不同子项目的多个不同模块组成。

JUnit 5 = JUnit 平台 + JUnit Jupiter + JUnit Vintage

JUnit Jupiter 和 JUnit Vintage 是 JUnit 平台上的测试引擎。就像 JUnit Suite(运行

@Suite
注释)和 Cucumber 一样。一般来说,这意味着 Jupiter 的一部分功能不适用于任何其他测试引擎。

例如查看

org.testcontainers.junit.jupiter.Testcontainers
的源代码时:

package org.testcontainers.junit.jupiter;

import org.junit.jupiter.api.extension.ExtendWith;
...
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(TestcontainersExtension.class)
@Inherited
public @interface Testcontainers {
...

您可以看到

TestContainers
带有
@ExtendWith(TestcontainersExtension.class)
元注释,这使其成为 Jupiter 扩展。而且因为扩展是来自 Jupiter 的概念,所以只有 Jupiter Engine 而不是 Vintage、Suite 或 Cucumber 可以理解扩展。

@TestContainers 与 Spring 结合使用速度很慢

使用

@TestContainers
时,将为每个方法创建一个新的测试容器。这很慢。此外,当使用 Spring Boot 时,这也意味着必须重新启动应用程序上下文。这会让事情变得更慢。

理想情况下,您可以为所有测试重用应用程序上下文和测试容器。这可以通过通过ContextCustomizerFactory启动测试容器并确保您创建的测试数据是唯一的来完成。

注意:虽然您可以使用

.withReuse(true)
来重用测试容器,但这意味着容器的范围不限于特定的应用程序上下文。因此,一个应用程序上下文(来自完全不同的项目)的详细信息可能会泄漏到另一个应用程序上下文中。

© www.soinside.com 2019 - 2024. All rights reserved.