如何基于 docker compose 和 testcontainers 设置 Spring Boot 的本地开发环境属性?

问题描述

我正在开发一个具有许多依赖项的服务,例如 Redis、PubSub、S3、ServiceC 和 ServiceD。当我在开发中遇到一个端点时,例如/data/4,对 ServiceC 执行 http 请求。

因此,为了工作,必须运行一些模拟 ServiceC 的东西。就我而言,它是wiremock(因为我不能直接运行 ServiceC,因为它也有很多依赖项)。所有这些 MockServices 都在一个 docker-compose-dev 文件中。

现在我的问题是:如何使用 testcontainers 运行 docker-compose,获取分配的端口,并将正确的属性设置为 WebClient 具有正确的模拟 url + 端口?

Spring Boot 启动前我可以使用什么生命周期钩子来运行,还可以配置属性

一个缺点是开发模式下的启动时间会增加,但我无法在 docker compose 文件中分配固定端口,因为它们可能会在开发人员的机器上使用。因此,在 localhost 上为服务 url 提供 url 认值是没有意义的。

解决方法

Testcontainers 支持使用 DockerComposeContainer 类开箱即用地启动 Docker Compose。创建此类的实例时,您必须公开所有容器:

@Testcontainers
public class DockerComposeTest {

  @Container
  public static DockerComposeContainer<?> environment =
    new DockerComposeContainer<>(new File("docker-compose.yml"))
      .withExposedService("database_1",5432,Wait.forListeningPort())
      .withExposedService("keycloak_1",8080,Wait.forHttp("/auth").forStatusCode(200)
        .withStartupTimeout(Duration.ofSeconds(30)));


  @Test
  void dockerComposeTest() {
    System.out.println(environment.getServicePort("database_1",5432));
    System.out.println(environment.getServicePort("keycloak_1",8080));
  }

}

上面的示例将 PostgreSQL 数据库和 Keycloak 映射到您机器上的随机临时端口。等待检查通过后,您的测试将立即运行,您可以使用 .getServicePort() 访问实际端口。

WebClient 配置的一个可能解决方案是使用 ApplicationContextInitializer,因为您必须在容器启动之前以某种方式定义 Spring Boot 属性:

public class PropertyInitializer
  implements ApplicationContextInitializer<ConfigurableApplicationContext> {
 
  @Override
  public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
 
    // static access to environment variable of the test
    TestPropertyValues
      .of("your_web_client_base_url=http://localhost:" + environment.getServicePort("serviceC",8080) + "/api")
      .applyTo(configurableApplicationContext);
  }
}

然后,您可以使用以下命令为您的集成测试注册初始化程序:

@Testcontainers
@ContextConfiguration(initializers = {PropertyInitializer.class})
class DockerComposeTest {
}

作为替代方案,您也可以尝试单独模拟与外部服务 with WireMock 的所有 HTTP 通信。