Testcontainer JDBC 支持

在这篇短文中,我们将了解 Testcontainers JDBC 支持,并比较在测试中启动Docker 容器的两种不同方式。

最初,我们将以编程方式管理 Testcontainer 的生命周期。之后,我们将通过单一配置属性简化此设置,并利用框架的 JDBC 支持。

手动管理测试容器生命周期
Testcontainers是一个提供轻量级一次性 Docker 容器用于测试的框架。我们可以使用它对真实服务(如数据库、消息队列或 Web 服务)运行测试,而无需模拟或外部依赖。

假设我们想要使用 Testcontainers 来验证与 PostgreSQL 数据库的交互。首先,我们将testcontainers依赖项添加到我们的pom.xml中:

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

之后,我们必须管理容器的生命周期,按照几个简单的步骤:
  • 创建容器对象
  • 在所有测试之前启动容器
  • 配置应用程序以连接容器
  • 测试结束时停止容器

我们可以使用 JUnit5 和 Spring Boot 注释(例如@BeforeAll、@AfterAll和@DynamicPropertyRegistry )自己实现这些步骤:

@SpringBootTest
class FullTestcontainersLifecycleLiveTest {
    static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:16-alpine")
      .withDatabaseName(
"test-db");
    @BeforeAll
    static void beforeAll() {
        postgres.start();
    }
    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        registry.add(
"spring.datasource.url", postgres::getJdbcUrl);
        registry.add(
"spring.datasource.username", postgres::getUsername);
        registry.add(
"spring.datasource.password", postgres::getPassword);
    }
    @AfterAll
    static void afterAll() {
        postgres.stop();
    }
   
// tests
}

尽管此解决方案允许我们自定义特定的生命周期阶段,但它需要复杂的设置。幸运的是,该框架提供了一种方便的解决方案,可以使用最少的配置启动容器并通过 JDBC 与它们通信。

使用 Testcontainers JDBC 驱动程序
当我们使用 JDBC 驱动程序时,Testcontainers 将自动启动托管我们数据库的 Docker 容器。为此,我们需要更新测试执行的 JDBC URL,并使用以下模式:“jdbc:tc:<docker-image-name>:<image-tag>:///<database-name>”。

让我们使用此语法在测试中更新spring.datasource.url :

spring.datasource.url: jdbc:tc:postgresql:16-alpine:///test-db


不用说,这个属性可以在专用的配置文件中定义,也可以通过@SpringBootTest注释在测试本身中定义:

@SpringBootTest(properties =
  "spring.datasource.url: jdbc:tc:postgresql:16-alpine:///test-db"
)
class CustomTestcontainersDriverLiveTest {
    @Autowired
    HobbitRepository theShire;
    @Test
    void whenCallingSave_thenEntityIsPersistedToDb() {
        theShire.save(new Hobbit(
"Frodo Baggins"));
        assertThat(theShire.findAll())
          .hasSize(1).first()
          .extracting(Hobbit::getName)
          .isEqualTo(
"Frodo Baggins");
    }
}

我们可以注意到,我们不再需要手动处理 PostgreSQL 容器的生命周期。测试容器处理了这种复杂性,使我们能够专注于手头的测试。