使用@Testcontainers后普通@SpringBootTest测试为何需要数据库连接?
问题根因
你遇到的报错本质是未加特殊配置的@SpringBootTest测试默认会加载完整Spring上下文,触发数据源、Liquibase自动配置,但你没有为这部分测试指定可用的测试数据源,Spring会直接读取application-test.yml/properties中配置的固定数据库地址,地址对应的容器关闭后自然连接失败。
你现有的AbstractDatabaseIT配置仅作用于继承它的集成测试类,普通测试类不会用到其中动态注入的Testcontainers数据源地址,所以问题和这个抽象类的配置本身无关。
解决方案
优化方案1:调整普通测试的注解,避免加载不必要的上下文
如果EthMessageVerifierTest这类测试不需要依赖数据库、不需要加载完整Spring Web上下文,直接去掉@SpringBootTest注解,改用@ExtendWith(MockitoExtension.class)做纯单元测试,或者用@SpringBootTest(classes = {EthMessageVerifier.class})只加载需要的Bean,不会触发数据源自动配置,自然不会报错,同时测试运行速度会大幅提升。
优化方案2:保留全上下文加载,配置测试专用数据源
如果确实需要加载完整Spring上下文,有两种可选方案:
方案A:普通测试用H2,集成测试用Testcontainers(推荐)
- 引入H2的test scope依赖
- 在
application-test.yml中配置H2数据源、H2适配的Liquibase配置(如果你的SQL脚本都是标准语法,无需额外调整Liquibase规则) - 现有集成测试不需要改动:
AbstractDatabaseIT中配置的@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)会覆盖默认的H2配置,优先使用Testcontainers动态生成的MySQL地址,两类测试的数据源互不冲突。
方案B:所有需要数据源的测试统一使用Testcontainers
你可以将Testcontainers的初始化逻辑抽为全局测试初始器,通过spring.factories注册到测试上下文,为所有@SpringBootTest测试统一注入动态的容器数据源地址:
- 把
MySQLContainer声明为全局静态单例,整个测试生命周期只会启动一次容器,不会额外浪费资源 - 所有测试都会使用真实MySQL环境,避免H2和生产数据库的兼容性差异,缺点是测试运行需要Docker环境,启动速度略慢于H2。
现有配置的小问题修正
你当前AbstractDatabaseIT中mySQLContainer.setParameters(parameters)的执行时机错误:容器已经启动后再设置参数不会生效,需要把参数配置逻辑移到mySQLContainer.start()之前执行。
关于要不要放弃Testcontainers的说明
不需要放弃,两者适用场景不同:
- H2优势是启动快、不需要Docker环境,缺点是和真实MySQL存在语法、特性兼容差异,如果你用到了MySQL专属函数、索引规则等,可能出现测试通过但生产报错的问题。
- Testcontainers优势是使用和生产版本一致的MySQL镜像,测试结果可信度高,缺点是依赖Docker环境、启动速度稍慢。
推荐的最佳实践是分层测试:
- 不需要依赖Spring上下文的纯业务逻辑测试用Mockito,速度最快
- 轻量依赖Spring上下文、不需要复杂数据库特性的测试用H2
- DAO层测试、全链路集成测试用Testcontainers,保证和生产行为一致
内容的提问来源于stack exchange,提问作者hc0re

