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

sky*_*yho 5 postgresql integration-testing docker spring-boot testcontainers

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

\n

根据文档(声明为静态字段的容器...),我试图确保我的容器在所有测试中都被引发一次。

\n

我专门使用它来获得Spring应用程序上下文和一次引发并用于所有测试的测试容器。

\n

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

\n
      boolean running = getPostgreSQLContainer().isRunning();\n\n        System.out.println(running);\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
    \n
  • pom.xml
  • \n
\n
    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.4.1</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n....\n\n<properties>\n        <java.version>11</java.version>\n        <testcontainers.version>1.15.1</testcontainers.version>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <version.mapstruct>1.4.1.Final</version.mapstruct>\n        <version.maven.compiler.plugin>3.8.1</version.maven.compiler.plugin>\n        <version.embedded.postgresql.testcontainers>1.86</version.embedded.postgresql.testcontainers>\n        <version.spring.cloud.starter>2.2.6.RELEASE</version.spring.cloud.starter>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>postgresql</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n            <version>${version.mapstruct}</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct-processor</artifactId>\n            <version>${version.mapstruct}</version>\n            <scope>provided</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-rest</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.liquibase</groupId>\n            <artifactId>liquibase-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.testcontainers</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.testcontainers</groupId>\n                <artifactId>testcontainers-bom</artifactId>\n                <version>${testcontainers.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 测试Postgres容器
  • \n
\n
@Testcontainers\n@TestPropertySource("classpath:application.properties")\npublic class TestPostgresContainer {\n\n    private static String dataBaseName;\n    private static String userNameBase;\n    private static String passwordBase;\n\n    public TestPostgresContainer() {\n    }\n\n    private static DockerImageName postgres;\n\n    static {\n        postgres = DockerImageName.parse("postgres:13.1");\n        dataBaseName = PropertiesExtractor.getProperty("database.name.test.container");\n        userNameBase = PropertiesExtractor.getProperty("username.testcontainer");\n        passwordBase = PropertiesExtractor.getProperty("password.testcontainer");\n    }\n\n    @SuppressWarnings("rawtypes")\n    @Container\n    private static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer(postgres)\n            .withDatabaseName(dataBaseName)\n            .withUsername(userNameBase)\n            .withPassword(passwordBase)\n            .withStartupTimeout(Duration.ofSeconds(600));\n\n\n    @SuppressWarnings("rawtypes")\n    public static PostgreSQLContainer getPostgreSQLContainer() {\n        return postgreSQLContainer;\n    }\n\n    /**\n     * It need for Spring boot 2.2.6 and higher.\n     */\n    @DynamicPropertySource\n    static void properties(DynamicPropertyRegistry propertyRegistry){\n\n        propertyRegistry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);\n        propertyRegistry.add("spring.datasource.username", postgreSQLContainer::getUsername);\n        propertyRegistry.add("spring.datasource.password", postgreSQLContainer::getPassword);\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 测试容器SpringBootClassruleApplicationTests
  • \n
\n
\n@RunWith(SpringRunner.class)\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\npublic class TestcontainersSpringBootClassruleApplicationTests extends TestPostgresContainer {\n\n    @Autowired\n    protected TestRestTemplate testRestTemplate;\n\n\n    @Test\n    @DisplayName("Should start the container")\n    public void test() {\n\n        boolean running = getPostgreSQLContainer().isRunning();\n\n        System.out.println(running);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 员工休息控制器测试
  • \n
\n
class EmployeeRestControllerTest extends TestcontainersSpringBootClassruleApplicationTests {\n\n    private static EmployeeDto employeeDto;\n\n    @BeforeAll\n   static void createUser(){\n\n        PostgreSQLContainer postgreSQLContainer = getPostgreSQLContainer();\n\n        employeeDto = EmployeeDto\n                .newBuilder()\n                .firstName("Joanna")\n                .lastName("Soyer")\n                .country("germany")\n                .build();\n    }\n\n    @Transactional\n    @Test\n    void addEmployee() {\n        boolean running = getPostgreSQLContainer().isRunning();\n\n        System.out.println(running);\n\n        String url = "/employees/addEmployee";\n\n        HttpEntity<EmployeeDto> entity = new HttpEntity<>(employeeDto);\n\n        ResponseEntity<EmployeeDto> employeeDtoResponseEntity =\n                testRestTemplate.exchange(url, HttpMethod.POST, entity, EmployeeDto.class);\n\n        HttpStatus statusCode = employeeDtoResponseEntity.getStatusCode();\n        assertThat(statusCode, is(HttpStatus.OK));\n    }\n\n    @Test\n    void getAllEmployees() {\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

测试类位于不同的目录中

\n
    \n
  • 应用程序属性
  • \n
\n
spring.main.banner-mode=off\nspring.datasource.initialization-mode=always\n\n## PostgreSQL for TestContainers\ndatabase.name.test.container=integration-tests-db\nusername.testcontainer=root\npassword.testcontainer=root\n\nspring.datasource.hikari.max-life = 600000 \n\nspring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true\n\nspring.liquibase.change-log=classpath:/db/changelog/db.changelog-master-test.xml\n
Run Code Online (Sandbox Code Playgroud)\n

我用的是liquibase

\n

Wnen 正在运行所有测试:

\n
\n

com.zaxxer.hikari.pool.PoolBase :HikariPool-1 - 无法验证连接 org.postgresql.jdbc.PgConnection@698af960 (\xd0\xa1\xd0\xbe\xd0\xb5\xd0\xb4\xd0\xb8\xd0 \xbd\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd1\x83\xd0\xb6\xd0\xb5 \xd0\xb1\xd1\x8b\xd0\xbb\xd0\xbe \xd0\xb7 \xd0\xb0\xd0\xba\xd1\x80\xd1\x8b\xd1\x82\xd0\xbe)。可能考虑使用较短的 maxLifetime 值。

\n
\n

我设置了一个值

\n
spring.main.banner-mode=off\nspring.datasource.initialization-mode=always\n\n## PostgreSQL for TestContainers\ndatabase.name.test.container=integration-tests-db\nusername.testcontainer=root\npassword.testcontainer=root\n\nspring.datasource.hikari.max-life = 600000 \n\nspring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true\n\nspring.liquibase.change-log=classpath:/db/changelog/db.changelog-master-test.xml\n
Run Code Online (Sandbox Code Playgroud)\n

这没有帮助。

\n

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

\n

我找到了这个:

\n
\n

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

\n

jdbc:tc:mysql:5.7.22:///数据库名称?TC_DAEMON=true

\n
\n

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

\n

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

\n

使用此参数,即使没有打开的连接,数据库容器也将继续运行。

\n

更新

\n

我编辑了这个:

\n
spring.datasource.hikari.max-life = 600000\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • \n
\n
@Container\n    private static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer(postgres)\n            .withDatabaseName(dataBaseName)\n            .withUsername(userNameBase)\n            .withPassword(passwordBase);\n
Run Code Online (Sandbox Code Playgroud)\n

这也没有帮助。

\n

我已经没有主意了。

\n

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

\n

rie*_*pil 4

您正在使用 JUnit Jupiter 扩展 ( @Testcontainers) 来控制容器的生命周期。该扩展支持两种模式

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

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

由于您的问题中存在所有代码层次结构和代码更新,因此很难说问题来自何处。

如果您想重用数据库容器并且只启动一次,请查看 Singleton 容器可重用性功能

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