TestContainers 和错误:“无法验证连接 org.postgresql.jdbc.PgConnection”为所有测试类提升单个容器

如何解决TestContainers 和错误:“无法验证连接 org.postgresql.jdbc.PgConnection”为所有测试类提升单个容器

当我尝试一一运行测试时遇到问题。 数据库连接已关闭。

根据文档 (Containers declared as static fields ...),我尝试确保我的容器在所有测试中都被提升一次。

我专门使用它来为 Spring 和一个 test-container 设置应用程序上下文 一次并用于所有测试。 >

确实如此,因为我会在每次测试中进行检查:

      boolean running = getPostgreSQLContainer().isRunning();

        System.out.println(running);

也就是说,测试是一项一项自动运行的。

  • pom.xml
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

....

<properties>
        <java.version>11</java.version>
        <testcontainers.version>1.15.1</testcontainers.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <version.mapstruct>1.4.1.Final</version.mapstruct>
        <version.maven.compiler.plugin>3.8.1</version.maven.compiler.plugin>
        <version.embedded.postgresql.testcontainers>1.86</version.embedded.postgresql.testcontainers>
        <version.spring.cloud.starter>2.2.6.RELEASE</version.spring.cloud.starter>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>postgresql</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${version.mapstruct}</version>
        </dependency>

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${version.mapstruct}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.testcontainers</groupId>
                <artifactId>testcontainers-bom</artifactId>
                <version>${testcontainers.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • TestPostgresContainer
@Testcontainers
@TestPropertySource("classpath:application.properties")
public class TestPostgresContainer {

    private static String dataBaseName;
    private static String userNameBase;
    private static String passwordBase;

    public TestPostgresContainer() {
    }

    private static DockerImageName postgres;

    static {
        postgres = DockerImageName.parse("postgres:13.1");
        dataBaseName = PropertiesExtractor.getProperty("database.name.test.container");
        userNameBase = PropertiesExtractor.getProperty("username.testcontainer");
        passwordBase = PropertiesExtractor.getProperty("password.testcontainer");
    }

    @SuppressWarnings("rawtypes")
    @Container
    private static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer(postgres)
            .withDatabaseName(dataBaseName)
            .withUsername(userNameBase)
            .withPassword(passwordBase)
            .withStartupTimeout(Duration.ofSeconds(600));


    @SuppressWarnings("rawtypes")
    public static PostgreSQLContainer getPostgreSQLContainer() {
        return postgreSQLContainer;
    }

    /**
     * It need for Spring boot 2.2.6 and higher.
     */
    @DynamicPropertySource
    static void properties(DynamicPropertyRegistry propertyRegistry){

        propertyRegistry.add("spring.datasource.url",postgreSQLContainer::getJdbcUrl);
        propertyRegistry.add("spring.datasource.username",postgreSQLContainer::getUsername);
        propertyRegistry.add("spring.datasource.password",postgreSQLContainer::getPassword);
    }

  • TestcontainersSpringBootClassruleApplicationTests

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestcontainersSpringBootClassruleApplicationTests extends TestPostgresContainer {

    @Autowired
    protected TestRestTemplate testRestTemplate;


    @Test
    @DisplayName("Should start the container")
    public void test() {

        boolean running = getPostgreSQLContainer().isRunning();

        System.out.println(running);
    }
}
  • EmployeeRestControllerTest
class EmployeeRestControllerTest extends TestcontainersSpringBootClassruleApplicationTests {

    private static EmployeeDto employeeDto;

    @BeforeAll
   static void createUser(){

        PostgreSQLContainer postgreSQLContainer = getPostgreSQLContainer();

        employeeDto = EmployeeDto
                .newBuilder()
                .firstName("Joanna")
                .lastName("Soyer")
                .country("germany")
                .build();
    }

    @Transactional
    @Test
    void addEmployee() {
        boolean running = getPostgreSQLContainer().isRunning();

        System.out.println(running);

        String url = "/employees/addEmployee";

        HttpEntity<EmployeeDto> entity = new HttpEntity<>(employeeDto);

        ResponseEntity<EmployeeDto> employeeDtoResponseEntity =
                testRestTemplate.exchange(url,HttpMethod.POST,entity,EmployeeDto.class);

        HttpStatus statusCode = employeeDtoResponseEntity.getStatusCode();
        assertThat(statusCode,is(HttpStatus.OK));
    }

    @Test
    void getAllEmployees() {
    }
}

测试类位于不同的目录中

  • application.properties
spring.main.banner-mode=off
spring.datasource.initialization-mode=always

## PostgreSQL for TestContainers
database.name.test.container=integration-tests-db
username.testcontainer=root
password.testcontainer=root

spring.datasource.hikari.max-life = 600000 

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master-test.xml

我使用 liquibase

Wnen 正在运行所有测试:

com.zaxxer.hikari.pool.PoolBase : HikariPool-1 - 无法验证连接 org.postgresql.jdbc.PgConnection@698af960 (Соединение уже было закрыто)。可能考虑使用较短的 maxLifetime 值。

我设置了一个值

spring.datasource.hikari.max-life = 600000

没用。

但是当我一次运行一个测试类时,就没有错误。

我发现了这个:

在守护进程模式下运行容器 默认情况下,只要最后一个连接关闭,数据库容器就会停止。在某些情况下,您可能需要启动容器并保持它运行,直到您明确停止它或 JVM 关闭。为此,请将 TC_DAEMON 参数添加到 URL,如下所示:

jdbc:tc:mysql:5.7.22:///databasename?TC_DAEMON=true

但是,我在哪里可以将 TC_DAEMON=true 添加到?

我没有直接指定url本身..它是由TestContainers完成的。

有了这个参数,即使没有打开的连接,数据库容器也会继续运行。

更新

我编辑了这个:

@Container
    private static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(postgres)
            .withDatabaseName(dataBaseName)
            .withUsername(userNameBase)
            .withPassword(passwordBase);
    @DynamicPropertySource
    static void registerPgProperties(DynamicPropertyRegistry propertyRegistry){

        String jdbcUrlPart = getPostgreSQLContainer().getJdbcUrl();
        String jdbcUrlFull = jdbcUrlPart + "&TC_DAEMON=true";

        propertyRegistry.add("integration-tests-db",getPostgreSQLContainer()::getDatabaseName);
        propertyRegistry.add("spring.datasource.username",getPostgreSQLContainer()::getUsername);
        propertyRegistry.add("spring.datasource.password",getPostgreSQLContainer()::getPassword);
        propertyRegistry.add("spring.datasource.url",() -> jdbcUrlFull);
    }

那也没有用。

我的想法已经用完了。

有人知道如何解决这个问题吗?

解决方法

您正在使用控制容器生命周期的 JUnit Jupiter 扩展 (@Testcontainers)。此扩展支持two modes

  1. 为每个测试方法重新启动的容器
  2. 在测试类的所有方法之间共享的容器

因此,对于您的设置,Testcontaienrs 为每个测试类启动一个数据库,该数据库在多个测试类之间共享,而是在同一测试类中的测试方法共享。

由于您问题中的所有代码层次结构和代码更新,很难说问题出在哪里。

如果您想重复使用数据库容器并且只启动一次,请查看 Singelton containersreusability feature

PS:您不需要 @RunWith(SpringRunner.class),因为您正在使用 JUnit 5 运行测试并且 JUnit Jupiter 扩展 (SpringExtension) 已经注册为 @SpringBootTest

的一部分> ,

解决方案

public class TestPostgresContainer {

    @SuppressWarnings("rawtypes")
    private static PostgreSQLContainer postgreSQLContainer;

    public TestPostgresContainer() {
    }


    static {
        DockerImageName postgres = DockerImageName.parse("postgres:13.1");

        postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer(postgres)
                .withReuse(true);

        postgreSQLContainer.start();
    }
....

   @SuppressWarnings("unused")
    @DynamicPropertySource
    static void registerPgProperties(DynamicPropertyRegistry propertyRegistry) {

        String jdbcUrl = getPostgreSQLContainer().getJdbcUrl();

        propertyRegistry.add("integration-tests-db",getPostgreSQLContainer()::getDatabaseName);
        propertyRegistry.add("spring.datasource.username",getPostgreSQLContainer()::getUsername);
        propertyRegistry.add("spring.datasource.password",getPostgreSQLContainer()::getPassword);
        propertyRegistry.add("spring.datasource.url",() -> jdbcUrl);
    }

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestcontainersSpringBootClassruleApplicationTests extends TestPostgresContainer {

    @Autowired
    protected TestRestTemplate testRestTemplate;

    @Test
    @DisplayName("Should start the container")
    public void test() {

        boolean running = getPostgreSQLContainer().isRunning();

        System.out.println("Is The container run : " + running);
    }

}
class EmployeeRestControllerTest extends TestcontainersSpringBootClassruleApplicationTests {

    private static EmployeeDto employeeDto;

   @BeforeAll
   static void createUser(){
...
public class EmployeeReadRestControllerTest extends TestcontainersSpringBootClassruleApplicationTests {

    @Test
    void getAllEmployees() {
  1. 我没有使用注释@Testcontainers

  2. 我没有使用resources/testcontainers.properties(我不太明白为什么需要这个文件。毕竟,一切正常。)

  3. 这很重要:

## PostgreSQL for TestContainers
database.name.test.container=integration-tests-db
username.testcontainer=root
password.testcontainer=root
 @DynamicPropertySource
    static void registerPgProperties(DynamicPropertyRegistry propertyRegistry) {

        String jdbcUrl = getPostgreSQLContainer().getJdbcUrl();

        propertyRegistry.add("integration-tests-db",() -> jdbcUrl);
    }
  1. 这不是必需的(至少不需要运行 Testcontainers)。我不知道为什么。
@TestPropertySource("classpath:application.properties")

取而代之的是 @DynamicPropertySource

我想添加一些评论:

  • 1 如果您使用 .withReuse(true) 方法,则在您完成后运行测试。您必须从容器中清除 docker 空间。

在这种情况下,Ryuk 不会为您做这件事。

 postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer(postgres)
      .withDatabaseName("test")
      .withUsername("name")
      .withPassword("password")
      .withReuse(true);
    1. 如果您只想增加运行测试的时间,那么我建议使用 Singleton containers。 在这种情况下,Ryuk 会为您代劳。

谢谢。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-