如何解决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:
- 为每个测试方法重新启动的容器
- 在测试类的所有方法之间共享的容器
因此,对于您的设置,Testcontaienrs 为每个测试类启动一个数据库,该数据库不在多个测试类之间共享,而是在同一测试类中的测试方法共享。
由于您问题中的所有代码层次结构和代码更新,很难说问题出在哪里。
如果您想重复使用数据库容器并且只启动一次,请查看 Singelton containers 和 reusability 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() {
-
我没有使用注释@Testcontainers
-
我没有使用resources/testcontainers.properties(我不太明白为什么需要这个文件。毕竟,一切正常。)
-
这很重要:
## 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);
}
- 这不是必需的(至少不需要运行 Testcontainers)。我不知道为什么。
@TestPropertySource("classpath:application.properties")
取而代之的是 @DynamicPropertySource。
我想添加一些评论:
- 1 如果您使用 .withReuse(true) 方法,则在您完成后运行测试。您必须从容器中清除 docker 空间。
在这种情况下,Ryuk 不会为您做这件事。
postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer(postgres)
.withDatabaseName("test")
.withUsername("name")
.withPassword("password")
.withReuse(true);
-
- 如果您只想增加运行测试的时间,那么我建议使用 Singleton containers。 在这种情况下,Ryuk 会为您代劳。
谢谢。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。