在这篇短文中,我们将了解 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:::///”。
让我们使用此语法在测试中更新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 容器的生命周期。测试容器处理了这种复杂性,使我们能够专注于手头的测试。