Spring Boot 如何使用 Testcontainers 支持在集成测试中自动管理数据库容器【免费下载链接】spring-bootSpring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.项目地址: https://gitcode.com/gh_mirrors/sp/spring-boot当你写的集成测试需要依赖真实的 MySQL、PostgreSQL、MongoDB 等后端服务时手动启动数据库再手动配置连接地址既麻烦又容易出错。Spring Boot 提供了spring-boot-testcontainers模块把 Testcontainers 库管理的 Docker 容器接入测试上下文容器在测试运行前启动、测试结束后停止并且连接细节地址、端口等可以自动注入给应用的自动配置无需手写application.properties。Spring Boot 文档给出的几种管理容器的方式都围绕同一个目标展开在测试开始前保证容器已启动在测试框架允许的范围内容器保持可用测试上下文关闭时自动停止容器。官方参考文档位于 testcontainers.adoc。前置条件使用 JUnit 5示例中的SpringBootTest与 JUnit extension 都基于它。本地有可用的 Docker 环境因为容器实例实际运行在 Docker 中。若要使用 service connectionServiceConnection文档明确要求把spring-boot-testcontainers模块作为测试依赖添加。该模块源码位于 spring-boot-testcontainers 目录下其中包含处理服务连接注解的ContainerConnectionDetailsFactory实现。方式一用 ServiceConnection 让自动配置直连容器数据库场景推荐路径这是数据库容器场景最直接的方式。在测试类中用 JUnit 注解声明一个static容器字段再加上ServiceConnectionSpring Boot 会自动创建对应的ConnectionDetailsbean交给对应技术的自动配置使用。文档给出的示例是一个 Neo4j 容器Testcontainers SpringBootTest class MyIntegrationTests { Container ServiceConnection static Neo4jContainer neo4j new Neo4jContainer(neo4j:5); Test void myTest() { System.out.println(neo4j); } }同一写法用在关系型数据库容器上同样适用。例如把字段换成PostgreSQLContainer或MySQLContainer均来自 Testcontainers JDBC 模块Spring Boot 会按容器类型匹配到对应的连接工厂。文档中的匹配关系如下JdbcConnectionDetails匹配JdbcDatabaseContainer类型MySQLContainer、PostgreSQLContainer、MariaDBContainer、MSSQLServerContainer、ClickHouseContainer、Oracle 等都属于该体系R2dbcConnectionDetails匹配ClickHouseContainer、MariaDBContainer、MSSQLServerContainer、MySQLContainer、Oraclefree 与 XE和PostgreSQLContainer等容器。文档特别提醒除RabbitStreamConnectionDetails外一个容器适用的所有连接细节 bean 默认都会被创建——PostgreSQLContainer会同时创建JdbcConnectionDetails和R2dbcConnectionDetails。如果你只想创建其中一部分用ServiceConnection的type属性限定。创建出的连接细节会覆盖连接相关的配置属性auto-configuration 消费 service connection 时连接细节优先于任何连接相关的配置属性生效所以你不需要在测试配置里手写 URL 和密码。完整示例代码见 serviceconnections/MyIntegrationTests.java。容器如何被识别文档说明了ServiceConnection匹配连接细节的两种机制static 字段上面的写法Spring Boot 能拿到Container实例默认用Container.getDockerImageName().getRepository()找连接细节即 Docker 镜像名去掉 registry 和版本号后的部分。Bean方法返回容器Spring Boot 不会调用该 bean 方法去取镜像名会造成过早初始化而是用 bean 方法的返回类型来判断。这要求使用Neo4jContainer、RabbitMQContainer这类类型化容器如果返回类型是GenericContainerSpring Boot 无法判断用了哪个镜像必须在注解上补充name属性例如文档给出的 Redis 示例TestConfiguration(proxyBeanMethods false) public class MyRedisConfiguration { Bean ServiceConnection(name redis) public GenericContainer? redisContainer() { return new GenericContainer(redis:7); } }使用自定义镜像比如公司内部 registry 的镜像registry.mycompany.com/mirror/myredis时也可以用ServiceConnection(name redis)覆盖默认识别逻辑确保创建DataRedisConnectionDetails。方式二把容器声明为 Spring Bean当容器需要和 Spring 的 bean 生命周期严格配合时比如应用 bean 要依赖容器提供的功能文档建议把容器声明为 Spring bean。在测试配置类中用Bean方法声明TestConfiguration(proxyBeanMethods false) class MyTestConfiguration { Bean MongoDBContainer mongoDbContainer() { return new MongoDBContainer(DockerImageName.parse(mongo:5.0)); } }然后在测试类中导入该配置类并注入容器SpringBootTest Import(MyTestConfiguration.class) class MyIntegrationTests { Autowired private MongoDBContainer mongo; Test void myTest() { System.out.println(this.mongo); } }示例代码见 springbeans/MyTestConfiguration.java 和 springbeans/MyIntegrationTests.java。文档指出这种方式常与 service connection 注解组合使用容器 bean 上同样可以加ServiceConnection。方式三复用容器声明接口多个测试类要用同一批容器时常见的做法是把容器声明为某个接口里的static字段interface MyContainers { Container MongoDBContainer mongoContainer new MongoDBContainer(mongo:5.0); Container Neo4jContainer neo4jContainer new Neo4jContainer(neo4j:5); }普通测试类直接实现该接口即可复用Spring Boot 测试则可以写一个配置类加上ImportTestcontainers注解把接口里的容器声明导入进来TestConfiguration(proxyBeanMethods false) ImportTestcontainers(MyContainers.class) class MyTestConfiguration { }示例代码见 importingconfigurationinterfaces/MyContainers.java 和 importingconfigurationinterfaces/MyTestConfiguration.java。容器生命周期什么时候启动、什么时候停止生命周期取决于容器由谁管理由 Testcontainers 注解管理TestcontainersContainer生命周期完全由 Testcontainers 负责static 字段在测试类运行完后停止非 static 字段在每个测试方法后停止。由 Spring bean 管理bean 在所有其他 bean 之前创建并启动在所有其他 bean 销毁之后停止。容器 bean 在每个由 TestContext Framework 管理的应用上下文内创建并启动一次随应用上下文关闭而停止——通常在所有使用同一缓存上下文的测试执行完之后发生也可能因 TestContext Framework 的缓存配置提前发生。这里有一个文档明确指出的坑使用 JUnit extension 时static 容器字段在测试类运行完后就被 Testcontainers 停止而 Spring 的 TestContext Framework 可能会缓存ApplicationContext并在另一个测试类或方法中复用。如果缓存的上下文里还有依赖已停止容器的 bean后续的测试或 bean 销毁回调可能失败。因此文档建议当应用上下文需要在其被缓存期间保持可用时优先把容器管理为 Spring bean或用ImportTestcontainers导入容器声明而不是依赖 JUnit extension 直接停止容器。此外文档说明单个容器实例在多个测试类之间共享和保留是常见且被支持的。替代方案用 DynamicPropertySource 手动传连接属性文档把DynamicPropertySource描述为 service connection 的一个更啰嗦但也更灵活的替代static 方法把动态属性值加入 Spring Environment自己指定属性名和取值来源Testcontainers SpringBootTest 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); } }当 service connection 无法匹配到你的容器比如自写的容器子类、匹配规则不生效时可以退回到这种方式手动把连接地址写入具体属性。示例见 dynamicproperties/MyIntegrationTests.java。验证与已知限制文档没有给出固定的成功日志格式实际验证落在测试行为本身测试运行时应用中的 bean例如 Neo4j/JDBC 相关 bean能够与容器内运行的服务通信说明连接细节已被自动配置消费容器随应用上下文关闭而停止。若连接失败优先核对两点——ServiceConnection是否能匹配到容器类型static 字段看镜像仓库名Bean方法看返回类型GenericContainer必须提供name属性以及是否遗漏了spring-boot-testcontainers测试依赖。边界方面文档明确了以下几点使用ServiceConnection前提是添加spring-boot-testcontainers测试依赖JUnit extension 管理容器时容器停止时机可能与缓存的应用上下文不一致可能导致后续测试或销毁回调抛异常service connection 的连接细节优先级高于连接相关配置属性测试中手写的连接配置会被覆盖排查连接问题时要意识到这一点。【免费下载链接】spring-bootSpring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.项目地址: https://gitcode.com/gh_mirrors/sp/spring-boot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考