Spring Boot 3.1中改进的Testcontainers支持

工程 | 莫里茨·哈尔布里特 | 2023年6月23日 | ...

Spring Boot一直支持Testcontainers一段时间了,而Spring Boot 3.1进一步改进了它。但首先,让我们看看Testcontainers是什么以及它通常是如何使用的。

Testcontainers是一个开源框架,用于提供可丢弃的、轻量级的数据库、消息代理、Web浏览器,或几乎任何可以在Docker容器中运行的东西的实例。

如果您过去使用过Testcontainers,那么您很有可能在集成测试中使用过它们

@SpringBootTest
@Testcontainers
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);
    }

}

在此集成测试中,一个 Neo4j 数据库将在 Testcontainer 中启动,并使用 @DynamicPropertySource 来配置 Spring Boot 以使用在容器中运行的 Neo4j 数据库。

在 Spring Boot 3.1 中,我们添加了两个与 Testcontainers 相关的新功能。这两个功能都是基于 ConnectionDetails 抽象实现的,我们在另一篇博文中进行了介绍。如果您还没有阅读,请立即阅读。这样,接下来的博文内容将会更有意义。

其中一个功能使 Testcontainers 的集成测试更加容易。新的 @ServiceConnection 注解可以用于您测试中的容器实例字段。

@SpringBootTest
@Testcontainers
class MyIntegrationTests {

    @Container
    @ServiceConnection
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

    @Test
    void myTest() {
        // ...
    }

}

这取代了对 @DynamicPropertySource 代码的需求,因此您可以直接将其删除。

在底层,@ServiceConnection 会发现被注解的容器类型,并为其创建一个 ConnectionDetails bean。在我们的示例中,这个 bean 将是 Neo4jConnectionDetails。Spring Boot 为 Neo4j 提供的自动配置将使用这个 bean,并配置驱动程序以连接到 Testcontainer 中运行的 Neo4j 服务器。这适用于 Testcontainers 支持的许多不同类型的容器。如果您使用的是 GenericContainer,我们会根据镜像名称来推断容器的类型。如果您使用的是我们不识别名称的自定义镜像,您可以使用 @ServiceConnection 注解的 name 属性来指明方向。

@ServiceConnection 注解容器字段有几个优点。首先,您需要输入的代码更少。其次,您的集成测试和 Spring Boot 自动配置之间不再存在通过属性进行的“字符串形式”的强耦合。第三,您无需查找(或记住)属性名称。

我们认为这是一个相当便捷的功能,足以升级到 Spring Boot 3.1。如果您还没有被说服,让我们向您展示另一个很棒的功能:开发时的 Testcontainers。

开发时的 Testcontainers

大多数应用程序都需要某种外部服务,例如 PostgreSQL 数据库、Redis 服务器或 Zipkin 后端。通常,这些服务是通过在接触代码之前从 README 文件运行一些 docker run 命令来提供的,或者您可以使用类似 Docker Compose 的工具(Spring Boot 3.1 为此也添加了一些很酷的新功能)。

通过开发时的 Testcontainers,您现在拥有了另一个工具。为什么只在集成测试中使用 Testcontainers?从技术上讲,没有什么可以阻止您在生产代码中启动 Testcontainers,然后设置属性来连接到这些容器。即使在使用 Spring Boot 3.1 之前的版本,现在也可以这样工作。

这种方法的缺点是,您现在需要在编译类路径上包含 Testcontainers 依赖项,并且很有可能这个依赖项也会包含在您的 fat JAR 中。在 Spring Boot 3.1 中,有更好的方法:将 Testcontainers 依赖项保留在 test 作用域中。您只需要在您的测试代码中创建一个新的 main 方法。

public class TestMyApplication {

    public static void main(String[] args) {
        SpringApplication.from(MyApplication::main).run(args);
    }

}

这个测试 main 方法使用新的 SpringApplication.from 方法将控制权委托给您生产代码中的“真实”main 方法。

您现在可以创建一个 @TestConfiguration,该配置定义了在开发应用程序时所需的 Testcontainers 的 bean。

@TestConfiguration(proxyBeanMethods = false)
class MyContainersConfiguration {

    @Bean
    @ServiceConnection
    Neo4jContainer<?> neo4jContainer() {
        return new Neo4jContainer<>("neo4j:5");
    }

}

请注意,此 bean 方法已用 @ServiceConnection 注解,以便 Spring Boot 自动建立与容器中运行的服务的连接。此容器的生命周期由 Spring Boot 管理。我们在应用程序启动时启动容器,并在应用程序停止时关闭它。

设置好这些后,回到您的测试 main 方法,并将其指向新创建的 @TestConfiguration

public class TestMyApplication {

    public static void main(String[] args) {
        SpringApplication.from(MyApplication::main)
            .with(MyContainersConfiguration.class)
            .run(args);
    }

}

现在您可以从 IDE 启动此测试 main 方法,容器会自动启动,Spring Boot 会与之建立连接。您无需设置任何配置属性,Spring Boot 会确保在应用程序停止时关闭容器。如果您更喜欢从终端运行应用程序,我们也能满足您的需求。Spring Boot 的 Gradle 和 Maven 插件已经学会运行此测试 main 方法。使用 Gradle,命令是 ./gradlew bootTestRun,使用 Maven,命令是 ./mvnw spring-boot:test-run

需要注意的一点是,每次重新启动应用程序时,容器都会关闭,并且它们会丢失数据。这可以通过两种方式解决:第一种是使用 Spring Boot devtools,然后为您的容器的 bean 方法加上 @RestartScope 注解。这些容器在 devtools 重新启动应用程序时**不会**重新启动。这意味着您不必每次更改应用程序中的某些内容时都等待容器启动,并且容器会保留其数据。

第二种方式是 Testcontainers 中的一个名为可重用容器的功能。

@TestConfiguration(proxyBeanMethods = false)
public class MyContainersConfiguration {

    @Bean
    @ServiceConnection
    public Neo4jContainer<?> neo4jContainer() {
        return new Neo4jContainer<>("neo4j:5").withReuse(true);
    }

}

这些容器在应用程序关闭时不会停止。这是一个 Testcontainers 的**实验性**功能,因此请自行承担风险使用。

为了完整起见,这是我们目前支持的容器列表。

  • CassandraContainer
  • CouchbaseContainer
  • ElasticsearchContainer
  • 使用 redisopenzipkin/zipkinGenericContainer
  • JdbcDatabaseContainer
  • KafkaContainer
  • MongoDBContainer
  • MariaDBContainer
  • MSSQLServerContainer
  • MySQLContainer
  • Neo4jContainer
  • OracleContainer
  • PostgreSQLContainer
  • RabbitMQContainer
  • RedpandaContainer

我们希望您喜欢这些新功能,并希望它们能帮助您编写更出色的应用程序。请阅读文档以开始使用,如果您遇到任何问题或有改进此功能的想法,请与我们联系

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有