Spring Boot 3.1 中 Testcontainers 支持的改进

工程 | Moritz Halbritter | 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,然后设置属性来连接到这些容器。即使在使用 3.1 之前的 Spring Boot 版本时,这也是可行的。

这种方法的缺点是您现在需要在编译类路径中包含 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 会确保在您的应用程序停止时关闭容器。如果您更喜欢从终端运行应用程序,我们也考虑到了这一点。Gradle 和 Maven 的 Spring Boot 插件学会了运行这个测试 main 方法。使用 Gradle,命令是 ./gradlew bootTestRun,使用 Maven,命令是 ./mvnw spring-boot:test-run

需要注意的是,每次重新启动应用程序时,容器都会关闭,并因此丢失数据。这可以通过两种方式解决:第一种是使用 Spring Boot devtools,然后使用 @RestartScope 注解您的容器 bean 方法。当 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 社区中即将举行的所有活动。

查看全部