如何 运行 为 spring 数据 elasticsearch 使用动态端口测试容器
how to run testcontainer with dynamic port for spring data elasticsearch
我的测试用例使用@SpringBootTest 注释来调出上下文并自动装配了一些存储库。 Testcontainer 在 @BeforeAll() 方法中启动。问题是 RestClientConfig 在测试用例中的 @BeforeAll() 之前是 initialized/injected。当 testcontainer 启动时,它会导出一些动态端口。
我必须在 testcontainer 34343 中设置一些固定端口,并在 RestClientConfig 的属性文件中使用相同的端口。
container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("discovery.type", "single-node")
.withExposedPorts(9200)
.withCreateContainerCmdModifier(cmd -> cmd.withHostConfig(
new HostConfig().withPortBindings(new PortBinding(Ports.Binding.bindPort(34343), new ExposedPort(9200)))));
有没有办法启动容器并获取其动态端口,然后使用它来初始化 RestClientConfig?
虽然我没有使用注释@Testcontainers。有必要吗?
- 您可以使用上下文配置初始化程序在运行时设置属性,稍后可以在 RestClientConfig 中使用。
让我向您展示 Postgresql 容器设置的示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(initializers = AbstractTestcontainersTest.DockerPostgreDataSourceInitializer.class)
public abstract class AbstractTestcontainersTest {
protected static final String DB_CONTAINER_NAME = "postgres-auth-test";
protected static PostgreSQLContainer<?> postgreDBContainer =
new PostgreSQLContainer<>(DockerImageName.parse("public.ecr.aws/docker/library/postgres:12.10-alpine")
.asCompatibleSubstituteFor("postgres"))
.withUsername("postgres")
.withPassword("change_me")
.withInitScript("db.sql")
.withCreateContainerCmdModifier(cmd -> cmd.withName(DB_CONTAINER_NAME))
.withDatabaseName("zpot_main");
@BeforeAll
public static void beforeAll() throws ShellExecutionException {
postgreDBContainer.start();
}
@AfterAll
public static void afterAll() {
postgreDBContainer.stop();
}
public static class DockerPostgreDataSourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.datasource.url=" + postgreDBContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreDBContainer.getUsername(),
"spring.datasource.password=" + postgreDBContainer.getPassword()
);
}
}
}
所有配置都在 DockerPostgreDataSourceInitializer
中完成,我在这里设置了我需要的所有属性。您还需要使用 @ContextConfiguration
注释对您的测试 class 进行注释。您可以对 ElasticSearchContainer 执行类似的操作。正如我刚刚检查的那样,ElasticSearchContainer 有一个方法 getHttpHostAddress()
which returns host+dynamic_port combination for your container。您可以获得 host-port 对并在属性中设置,以便稍后在您的客户端配置中使用。如果您只需要端口,您可以调用 container.getMappedPort(9200)
并再次在属性中设置该端口。
- 关于@Testcontainers注解,如果你想让testcontainers管理你的容器生命周期,你需要它。在这种情况下,您还需要使用 @Container 注释来注释容器。如果您的容器是静态字段,则您的容器将在 class 中的所有测试方法之前启动一次,如果它是常规字段,则将在每个测试方法之前启动一次。您可以在此处阅读更多相关信息:https://www.testcontainers.org/test_framework_integration/junit_5/#extension。
或者您可以在 @BeforeAll 或 @BeforeEach 注释设置方法中手动启动容器。换句话说,不,您不必使用
@Testcontainers
注释。
较新版本的 Spring 正好针对此用例提供 @DynamicPropertySource
:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/DynamicPropertySource.html
您的代码应大致如下所示:
@SpringJUnitConfig(...)
@Testcontainers
class ExampleIntegrationTests {
@Container
static ElasticsearchContainer elastic= new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("discovery.type", "single-node");
// ...
@DynamicPropertySource
static void elasticProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.rest.uris", elastic::getHttpHostAddress);
}
}
我的测试用例使用@SpringBootTest 注释来调出上下文并自动装配了一些存储库。 Testcontainer 在 @BeforeAll() 方法中启动。问题是 RestClientConfig 在测试用例中的 @BeforeAll() 之前是 initialized/injected。当 testcontainer 启动时,它会导出一些动态端口。
我必须在 testcontainer 34343 中设置一些固定端口,并在 RestClientConfig 的属性文件中使用相同的端口。
container = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("discovery.type", "single-node")
.withExposedPorts(9200)
.withCreateContainerCmdModifier(cmd -> cmd.withHostConfig(
new HostConfig().withPortBindings(new PortBinding(Ports.Binding.bindPort(34343), new ExposedPort(9200)))));
有没有办法启动容器并获取其动态端口,然后使用它来初始化 RestClientConfig?
虽然我没有使用注释@Testcontainers。有必要吗?
- 您可以使用上下文配置初始化程序在运行时设置属性,稍后可以在 RestClientConfig 中使用。
让我向您展示 Postgresql 容器设置的示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
@ContextConfiguration(initializers = AbstractTestcontainersTest.DockerPostgreDataSourceInitializer.class)
public abstract class AbstractTestcontainersTest {
protected static final String DB_CONTAINER_NAME = "postgres-auth-test";
protected static PostgreSQLContainer<?> postgreDBContainer =
new PostgreSQLContainer<>(DockerImageName.parse("public.ecr.aws/docker/library/postgres:12.10-alpine")
.asCompatibleSubstituteFor("postgres"))
.withUsername("postgres")
.withPassword("change_me")
.withInitScript("db.sql")
.withCreateContainerCmdModifier(cmd -> cmd.withName(DB_CONTAINER_NAME))
.withDatabaseName("zpot_main");
@BeforeAll
public static void beforeAll() throws ShellExecutionException {
postgreDBContainer.start();
}
@AfterAll
public static void afterAll() {
postgreDBContainer.stop();
}
public static class DockerPostgreDataSourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"spring.datasource.url=" + postgreDBContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreDBContainer.getUsername(),
"spring.datasource.password=" + postgreDBContainer.getPassword()
);
}
}
}
所有配置都在 DockerPostgreDataSourceInitializer
中完成,我在这里设置了我需要的所有属性。您还需要使用 @ContextConfiguration
注释对您的测试 class 进行注释。您可以对 ElasticSearchContainer 执行类似的操作。正如我刚刚检查的那样,ElasticSearchContainer 有一个方法 getHttpHostAddress()
which returns host+dynamic_port combination for your container。您可以获得 host-port 对并在属性中设置,以便稍后在您的客户端配置中使用。如果您只需要端口,您可以调用 container.getMappedPort(9200)
并再次在属性中设置该端口。
- 关于@Testcontainers注解,如果你想让testcontainers管理你的容器生命周期,你需要它。在这种情况下,您还需要使用 @Container 注释来注释容器。如果您的容器是静态字段,则您的容器将在 class 中的所有测试方法之前启动一次,如果它是常规字段,则将在每个测试方法之前启动一次。您可以在此处阅读更多相关信息:https://www.testcontainers.org/test_framework_integration/junit_5/#extension。
或者您可以在 @BeforeAll 或 @BeforeEach 注释设置方法中手动启动容器。换句话说,不,您不必使用
@Testcontainers
注释。
较新版本的 Spring 正好针对此用例提供 @DynamicPropertySource
:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/DynamicPropertySource.html
您的代码应大致如下所示:
@SpringJUnitConfig(...)
@Testcontainers
class ExampleIntegrationTests {
@Container
static ElasticsearchContainer elastic= new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
.withEnv("discovery.type", "single-node");
// ...
@DynamicPropertySource
static void elasticProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.rest.uris", elastic::getHttpHostAddress);
}
}