Testcontainers
The Testcontainers 庫提供了一種管理在 Docker 容器內執行的服務的方法。它與 JUnit 整合,允許你編寫一個測試類,在任何測試執行之前啟動容器。Testcontainers 對於編寫與真實後端服務(如 MySQL、MongoDB、Cassandra 等)互動的整合測試特別有用。
Testcontainers 可按如下方式用於 Spring Boot 測試
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
@JvmStatic
val neo4j = Neo4jContainer("neo4j:5");
}
}
這將在執行任何測試之前啟動一個執行 Neo4j 的 docker 容器(如果 Docker 在本地執行)。在大多數情況下,你需要配置應用程式以連線到容器中執行的服務。
服務連線
服務連線是與任何遠端服務的連線。Spring Boot 的自動配置可以消費服務連線的詳細資訊,並使用它們來建立與遠端服務的連線。在此過程中,連線詳細資訊優先於任何連線相關的配置屬性。
使用 Testcontainers 時,可以透過在測試類中註解容器欄位來自動為容器中執行的服務建立連線詳細資訊。
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
@ServiceConnection
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
}
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
@ServiceConnection
@JvmStatic
val neo4j = Neo4jContainer("neo4j:5");
}
}
得益於 @ServiceConnection
,上述配置允許應用程式中與 Neo4j 相關的 Bean 與 Testcontainers 管理的 Docker 容器中執行的 Neo4j 進行通訊。這是透過自動定義一個 Neo4jConnectionDetails
Bean 來完成的,該 Bean 隨後被 Neo4j 自動配置使用,覆蓋了任何連線相關的配置屬性。
你需要將 spring-boot-testcontainers 模組作為測試依賴項新增,以便與 Testcontainers 一起使用服務連線。 |
服務連線註解由在 spring.factories
中註冊的 ContainerConnectionDetailsFactory
類處理。ContainerConnectionDetailsFactory
可以基於特定的 Container
子類或 Docker 映象名稱建立 ConnectionDetails
Bean。
spring-boot-testcontainers
jar 中提供了以下服務連線工廠
連線詳細資訊 | 匹配規則 |
---|---|
命名為 "symptoma/activemq" 的容器或 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
命名為 "otel/opentelemetry-collector-contrib" 或 |
|
命名為 "otel/opentelemetry-collector-contrib" 或 |
|
命名為 "otel/opentelemetry-collector-contrib" 或 |
|
|
|
|
|
|
|
|
|
命名為 "openzipkin/zipkin" 的容器 |
預設情況下,將為給定的 如果你只想建立適用型別的一個子集,可以使用 |
預設情況下,使用 Container.getDockerImageName().getRepository()
獲取用於查詢連線詳細資訊的名稱。Docker 映象名稱的 repository 部分會忽略任何 registry 和版本。只要 Spring Boot 能夠獲取 Container
的例項,就像上面示例中使用的 static
欄位一樣,這種方式就有效。
如果你使用 @Bean
方法,Spring Boot 不會呼叫 bean 方法來獲取 Docker 映象名稱,因為這會導致早期初始化問題。相反,會使用 bean 方法的返回型別來確定應使用哪種連線詳細資訊。只要你使用像 Neo4jContainer
或 RabbitMQContainer
這樣的型別化容器,這種方式就有效。如果你使用 GenericContainer
,例如下面的 Redis 示例所示,這種方式就無效了
-
Java
-
Kotlin
import org.testcontainers.containers.GenericContainer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
@TestConfiguration(proxyBeanMethods = false)
public class MyRedisConfiguration {
@Bean
@ServiceConnection(name = "redis")
public GenericContainer<?> redisContainer() {
return new GenericContainer<>("redis:7");
}
}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.GenericContainer
@TestConfiguration(proxyBeanMethods = false)
class MyRedisConfiguration {
@Bean
@ServiceConnection(name = "redis")
fun redisContainer(): GenericContainer<*> {
return GenericContainer("redis:7")
}
}
Spring Boot 無法從 GenericContainer
判斷使用了哪個容器映象,因此必須使用 @ServiceConnection
的 name
屬性來提供該提示。
你還可以使用 @ServiceConnection
的 name
屬性來覆蓋將使用的連線詳細資訊,例如在使用自定義映象時。如果你使用的是 Docker 映象 registry.mycompany.com/mirror/myredis
,你會使用 @ServiceConnection(name="redis")
來確保建立 RedisConnectionDetails
。
動態屬性
@DynamicPropertySource
是服務連線的一種稍微更冗長但也更靈活的替代方案。靜態 @DynamicPropertySource
方法允許向 Spring Environment 新增動態屬性值。
-
Java
-
Kotlin
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");
@Test
void myTest() {
// ...
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
}
}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
@Testcontainers
@SpringBootTest
class MyIntegrationTests {
@Test
fun myTest() {
// ...
}
companion object {
@Container
@JvmStatic
val neo4j = Neo4jContainer("neo4j:5");
@DynamicPropertySource
@JvmStatic
fun neo4jProperties(registry: DynamicPropertyRegistry) {
registry.add("spring.neo4j.uri") { neo4j.boltUrl }
}
}
}
上述配置允許應用程式中與 Neo4j 相關的 Bean 與 Testcontainers 管理的 Docker 容器中執行的 Neo4j 進行通訊。