如何在JUnit测试代码中向测试容器启动的数据库插入数据
JUnit测试前插入Postgres测试数据的实现方案
针对你用Docker Compose配置的动态端口Postgres容器,下面是几种实用的实现方式:
1. 用Testcontainers管理容器+JDBC直接插入数据
因为容器用了动态端口,硬编码端口会导致连接失败,Testcontainers可以帮你自动启动Docker Compose服务并获取实际映射端口。
第一步:添加依赖(Maven为例)
<!-- Testcontainers核心 + Docker Compose支持 --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>1.19.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>docker-compose</artifactId> <version>1.19.3</version> <scope>test</scope> </dependency> <!-- Postgres JDBC驱动 --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.6.0</version> <scope>test</scope> </dependency>
第二步:编写JUnit测试类
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.io.File; @Testcontainers public class FunctionalTestDemo { // 加载你的docker-compose.yml文件 @Container private static final DockerComposeContainer<?> composeContainer = new DockerComposeContainer<>(new File("docker-compose.yml")) .withExposedService("postgres", 5432); private static String jdbcUrl; private static final String DB_USER = "username"; private static final String DB_PASSWORD = "password"; @BeforeAll static void initContainerConnection() { // 获取Postgres容器的实际对外端口 Integer mappedPort = composeContainer.getServicePort("postgres", 5432); jdbcUrl = String.format("jdbc:postgresql://localhost:%d/myTestingDB", mappedPort); } @BeforeEach void insertTestRecords() throws SQLException { // 每次测试前插入测试数据,确保数据隔离 try (Connection conn = DriverManager.getConnection(jdbcUrl, DB_USER, DB_PASSWORD)) { // 示例:插入user表数据,ON CONFLICT避免重复插入 String insertSql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?) ON CONFLICT DO NOTHING"; try (PreparedStatement stmt = conn.prepareStatement(insertSql)) { stmt.setInt(1, 1); stmt.setString(2, "测试用户"); stmt.setString(3, "test@demo.com"); stmt.executeUpdate(); } // 可添加更多插入逻辑 } } @Test void testBusinessLogic() { // 这里编写你的功能测试代码,此时数据库已预加载测试数据 } }
2. Spring Boot项目简化方案
如果是Spring Boot项目,结合@Sql注解可以直接执行SQL脚本,无需手动写JDBC代码:
测试类示例
import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; import org.testcontainers.containers.PostgresContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers @SpringBootTest public class SpringFunctionalTest { // 直接启动Postgres容器(替代Docker Compose,也可以用Docker Compose方式) @Container static PostgresContainer<?> postgresContainer = new PostgresContainer<>("my-proxy.jfrog.io/postgres:12") .withDatabaseName("myTestingDB") .withUsername("username") .withPassword("password"); // 测试前自动执行classpath下的test-data.sql脚本 @Sql("/test-data.sql") @Test void testWithPreloadedData() { // 编写功能测试逻辑 } }
对应的SQL脚本(src/test/resources/test-data.sql)
INSERT INTO users (id, name, email) VALUES (1, '测试用户', 'test@demo.com') ON CONFLICT DO NOTHING; -- 可添加更多插入语句
关键注意事项
- 动态端口处理:Testcontainers自动管理端口映射,通过
getServicePort获取实际端口,避免硬编码导致的连接失败 - 数据隔离:用
@BeforeEach确保每次测试前数据一致,也可以在@AfterEach中执行清理SQL,或者开启事务回滚(Spring项目可用@Transactional) - 私有镜像认证:如果你的JFrog镜像需要认证,确保本地Docker已配置好镜像仓库凭证(在
~/.docker/config.json中添加认证信息)
内容的提问来源于stack exchange,提问作者Laodao
相关产品推荐
相关产品推荐

