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。Neo4j 的 Spring Boot 自动配置会使用此 bean 并配置驱动程序以连接到 Testcontainer 中运行的 Neo4j 服务器。这 适用于 Testcontainers 支持的许多不同容器类型。如果您使用的是 GenericContainer,我们将查看镜像名称以推断容器类型。如果您使用的是我们无法识别的自定义镜像,则可以使用 @ServiceConnection 注解的 name 属性来指引我们。

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

我们认为这是一个非常棒的功能,也是升级到 Spring Boot 3.1 的充分理由。如果您还没有被说服,让我们向您展示另一个很棒的功能:开发时使用 Testcontainers。

开发时使用 Testcontainers

大多数应用程序都需要某种外部服务,例如 PostgreSQL 数据库、Redis 服务器或 Zipkin 后端。通常,这些服务通过在接触代码之前从自述文件中运行一些 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 会确保在应用程序停止时关闭容器。如果您更喜欢从终端运行应用程序,我们也为您准备好了。Gradle 和 Maven 的 Spring Boot 插件学会了运行此测试 main 方法。对于 Gradle,它是 ./gradlew bootTestRun,对于 Maven,它是 ./mvnw spring-boot:test-run

需要注意的一件事是,每次重新启动应用程序时,您的容器都会关闭,并且它们会丢失数据。这可以通过两种方式解决:第一种是使用 Spring Boot 开发工具,然后使用 @RestartScope 注解容器的 bean 方法。当开发工具重新启动应用程序时,此类容器不会重新启动。这意味着您不必每次更改应用程序中的内容时都等待容器启动,并且容器会保留其数据。

第二种方法是 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 社区中所有即将举行的活动。

查看全部