如何用TestContainers替代Wix Embedded MySQL用于JUnit测试?
用TestContainers替换Wix Embedded MySQL配置
之前在JUnit测试中使用Wix Embedded MySQL,但该工具已不再支持最新版本的MySQL,现需切换到TestContainers,以下是替换原配置的具体方案,原配置类代码如下:
@Configuration @Profile({"mysql"}) @ConditionalOnProperty(prefix = "spring", name = "datasource.jdbcUrl") public class EmbeddedMySqlDbConfig { protected static Logger logger = LoggerFactory.getLogger(EmbeddedMySqlDbConfig.class); @Bean(name = "downloadConfig") DownloadConfig downloadConfig() { String downloadPath = "/app/mysql"; logger.info("Embedded MySQL download path is {}", downloadPath); DownloadConfig config = aDownloadConfig().withCacheDir(downloadPath).build(); return config; } @Bean(name = "mysqldConfig") MysqldConfig mysqldConfig(@Value("${spring.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${mysql.port}") int port) { MysqldConfig config = aMysqldConfig(v5_7_latest).withPort(port).withUser(dbUsername, dbPassword).build(); return config; } private void addSchema(String databaseName, Builder builder) { SchemaConfig schema = aSchemaConfig(databaseName).build(); builder.addSchema(schema); } @Bean(name = "embeddedMysql") EmbeddedMysql embeddedMysql(@Qualifier("downloadConfig") DownloadConfig downloadConfig, @Qualifier("mysqldConfig") MysqldConfig config, @Value("${mysql.databaseName}") String databaseName, @Value("${mysql.databaseName1}") String databaseName1) { try { Builder builder = new Builder(config, downloadConfig); for (int i = 0; i < 2; i++) { String name = databaseName; if (i == 1) { name = null; if (databaseName1 != null && databaseName1.trim().length() > 0) { name = databaseName1; } } if (name != null) { addSchema(name, builder); } } EmbeddedMysql mysql = builder.start(); logger.info("embedded mysql started"); return mysql; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw e; } } @Bean(name = "dataSource") @DependsOn("embeddedMysql") public DataSource dataSource(@Value("${spring.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${spring.datasource.jdbcUrl}") String url, @Value("${spring.datasource.driver-class-name}") String driverClassName) { DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.driverClassName(driverClassName); dataSourceBuilder.url(url); dataSourceBuilder.username(dbUsername); dataSourceBuilder.password(dbPassword); return dataSourceBuilder.build(); } @Bean(name = "requestDataSource") @ConditionalOnProperty(prefix = "request", name = "datasource.jdbcUrl") @DependsOn("dataSource") public DataSource requestDataSource(@Value("${request.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${request.datasource.jdbcUrl}") String url, @Value("${spring.datasource.driver-class-name}") String driverClassName) { DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.driverClassName(driverClassName); dataSourceBuilder.url(url); dataSourceBuilder.username(dbUsername); dataSourceBuilder.password(dbPassword); return dataSourceBuilder.build(); } }
替换步骤
1. 添加TestContainers依赖
根据项目构建工具选择对应的依赖:
Maven
<dependency> <groupId>org.testcontainers</groupId> <artifactId>mysql</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <!-- Spring Boot整合依赖(可选,简化配置) --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>spring-boot</artifactId> <scope>test</scope> <version>1.19.0</version> <!-- 使用最新稳定版本 --> </dependency>
Gradle
testImplementation 'org.testcontainers:mysql' testImplementation 'org.testcontainers:junit-jupiter' // Spring Boot整合依赖(可选) testImplementation 'org.testcontainers:spring-boot:1.19.0'
2. 替换原配置类
移除EmbeddedMySqlDbConfig,创建新的TestContainers配置类,实现相同的容器启动、数据库创建及数据源配置逻辑:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.utility.DockerImageName; import javax.sql.DataSource; import java.sql.Connection; import java.sql.Statement; @Configuration @Profile({"mysql"}) @ConditionalOnProperty(prefix = "spring", name = "datasource.jdbcUrl") public class TestcontainersMysqlConfig { private static final Logger logger = LoggerFactory.getLogger(TestcontainersMysqlConfig.class); @Bean(initMethod = "start", destroyMethod = "stop") public MySQLContainer<?> mysqlContainer( @Value("${spring.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${mysql.databaseName}") String databaseName, @Value("${mysql.databaseName1}") String databaseName1 ) { // 指定MySQL镜像版本,可替换为需要的版本(如8.0、5.7) MySQLContainer<?> container = new MySQLContainer<>(DockerImageName.parse("mysql:8.0")) .withUsername(dbUsername) .withPassword(dbPassword); // 创建配置的数据库 try (Connection conn = container.createConnection(""); Statement stmt = conn.createStatement()) { stmt.executeUpdate(String.format("CREATE DATABASE IF NOT EXISTS %s", databaseName)); if (databaseName1 != null && !databaseName1.trim().isEmpty()) { stmt.executeUpdate(String.format("CREATE DATABASE IF NOT EXISTS %s", databaseName1)); } logger.info("成功创建数据库:{}、{}", databaseName, databaseName1); } catch (Exception e) { logger.error("创建数据库失败", e); throw new RuntimeException(e); } logger.info("TestContainers MySQL容器启动完成"); return container; } @Bean(name = "dataSource") public DataSource dataSource( MySQLContainer<?> mysqlContainer, @Value("${spring.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${mysql.databaseName}") String databaseName ) { // 动态生成目标数据库的JDBC URL String jdbcUrl = mysqlContainer.getJdbcUrl().replace("/test", "/" + databaseName); return DataSourceBuilder.create() .driverClassName(mysqlContainer.getDriverClassName()) .url(jdbcUrl) .username(dbUsername) .password(dbPassword) .build(); } @Bean(name = "requestDataSource") @ConditionalOnProperty(prefix = "request", name = "datasource.jdbcUrl") public DataSource requestDataSource( MySQLContainer<?> mysqlContainer, @Value("${request.datasource.username}") String dbUsername, @Value("${spring.datasource.password}") String dbPassword, @Value("${mysql.databaseName1}") String databaseName1 ) { String jdbcUrl = mysqlContainer.getJdbcUrl().replace("/test", "/" + databaseName1); return DataSourceBuilder.create() .driverClassName(mysqlContainer.getDriverClassName()) .url(jdbcUrl) .username(dbUsername) .password(dbPassword) .build(); } }
3. 配置文件调整
- 移除原配置中的
mysql.port项,TestContainers会自动分配随机端口,避免端口冲突。 - 原配置文件中的
spring.datasource.jdbcUrl和request.datasource.jdbcUrl无需硬编码,会由配置类动态生成。
核心差异说明
- TestContainers基于Docker运行MySQL容器,自动管理镜像拉取、容器启停,无需手动维护MySQL安装包。
- 数据库创建逻辑通过JDBC直接执行SQL实现,替代原Wix工具的Schema配置。
- 数据源URL动态生成,适配容器的随机端口,无需担心环境不一致问题。
内容的提问来源于stack exchange,提问作者Shubham Shekhar
相关产品推荐
相关产品推荐

