非Spring环境下Testcontainers数据库测试实现求助
非Spring环境下用Testcontainers测试PostgreSQL数据库操作
问题描述
作业要求禁止使用Spring,目前正在学习Docker与Testcontainers。项目需要和数据库交互,已经通过代码创建了PostgreSQLContainer,但不知道怎么编写测试用例验证数据库的增、改、查操作。已实现三个数据库操作方法:添加事务、更新余额、打印审计数据,但找不到非Spring环境下的Testcontainers使用示例,特此求助。
已实现的方法代码
添加事务方法
@Override public void addTransaction(TypeOfTransaction type, int amount) { String sql = """ INSERT INTO entities."Transaction" (type, amount) VALUES (?, ?) """; try (Connection connection = ConnectionManager.open(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { preparedStatement.setString(1, String.valueOf(type)); preparedStatement.setInt(2, amount); preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } }
更新余额方法
@Override public void minusBalance(String login, int amount) { String sql = """ UPDATE entities."Client" SET balance = balance - ? WHERE login = ? """; try (Connection connection = ConnectionManager.open(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { preparedStatement.setInt(1, amount); preparedStatement.setString(2, login); preparedStatement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } }
查询打印审计数据方法
@Override public void printAudit(String login) { String sql = """ SELECT action FROM "Client_Audit" WHERE client_login = ? """; try (Connection connection = ConnectionManager.open(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { preparedStatement.setString(1, login); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getString("action")); } } catch (SQLException e) { throw new RuntimeException(e); } }
解决方案
1. 依赖配置(Maven)
确保测试依赖包含Testcontainers PostgreSQL模块和JUnit 5:
<dependencies> <!-- Testcontainers PostgreSQL --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>1.19.0</version> <scope>test</scope> </dependency> <!-- Testcontainers JUnit 5 集成 --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>1.19.0</version> <scope>test</scope> </dependency> <!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> </dependencies>
2. 调整ConnectionManager(支持测试配置)
如果你的ConnectionManager是硬编码连接参数,改成可动态配置的,方便测试时连接到容器数据库:
public class ConnectionManager { private static String url; private static String username; private static String password; // 供测试设置连接参数 public static void setConnectionParams(String dbUrl, String dbUsername, String dbPassword) { url = dbUrl; username = dbUsername; password = dbPassword; } public static Connection open() throws SQLException { return DriverManager.getConnection(url, username, password); } }
3. 编写测试类
使用JUnit 5 + Testcontainers管理容器,每个测试用例在干净的数据库环境中运行:
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import static org.junit.jupiter.api.Assertions.*; @Testcontainers public class DatabaseOperationsTest { // 初始化PostgreSQL容器,指定镜像版本 @Container private static final PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:15-alpine") .withDatabaseName("test_db") .withUsername("test_user") .withPassword("test_pass"); // 业务类实例 private TransactionService transactionService; private ClientService clientService; private AuditService auditService; @BeforeAll static void initContainerConnection() { postgresContainer.start(); // 配置ConnectionManager连接到测试容器 ConnectionManager.setConnectionParams( postgresContainer.getJdbcUrl(), postgresContainer.getUsername(), postgresContainer.getPassword() ); } @AfterAll static void stopContainer() { postgresContainer.stop(); } @BeforeEach void setUpTestEnvironment() throws SQLException { // 初始化业务类 transactionService = new TransactionServiceImpl(); clientService = new ClientServiceImpl(); auditService = new AuditServiceImpl(); // 创建测试所需的表结构 try (Connection conn = ConnectionManager.open()) { // 创建schemas和表 executeSql(conn, """ CREATE SCHEMA IF NOT EXISTS entities; CREATE TABLE IF NOT EXISTS entities."Transaction" ( id SERIAL PRIMARY KEY, type VARCHAR(255) NOT NULL, amount INT NOT NULL ); CREATE TABLE IF NOT EXISTS entities."Client" ( id SERIAL PRIMARY KEY, login VARCHAR(255) UNIQUE NOT NULL, balance INT DEFAULT 0 ); CREATE TABLE IF NOT EXISTS "Client_Audit" ( id SERIAL PRIMARY KEY, client_login VARCHAR(255) NOT NULL, action VARCHAR(255) NOT NULL ); """); // 插入测试用的客户端数据 executeSql(conn, """ INSERT INTO entities."Client" (login, balance) VALUES ('test_login', 1000); """); } } // 辅助方法:执行SQL语句 private void executeSql(Connection conn, String sql) throws SQLException { try (PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.executeUpdate(); } } // 测试添加事务 @Test void addTransaction_ShouldInsertData() throws SQLException { transactionService.addTransaction(TypeOfTransaction.DEPOSIT, 500); // 验证数据插入 try (Connection conn = ConnectionManager.open()) { ResultSet rs = conn.prepareStatement("SELECT type, amount FROM entities.\"Transaction\"") .executeQuery(); assertTrue(rs.next()); assertEquals(TypeOfTransaction.DEPOSIT.name(), rs.getString("type")); assertEquals(500, rs.getInt("amount")); assertFalse(rs.next()); } } // 测试更新余额 @Test void minusBalance_ShouldUpdateClientBalance() throws SQLException { clientService.minusBalance("test_login", 200); // 验证余额更新 try (Connection conn = ConnectionManager.open()) { ResultSet rs = conn.prepareStatement("SELECT balance FROM entities.\"Client\" WHERE login = 'test_login'") .executeQuery(); assertTrue(rs.next()); assertEquals(800, rs.getInt("balance")); } } // 测试打印审计数据 @Test void printAudit_ShouldRetrieveAuditData() throws SQLException { // 先插入一条审计数据模拟业务操作 executeSql(ConnectionManager.open(), """ INSERT INTO "Client_Audit" (client_login, action) VALUES ('test_login', 'BALANCE_UPDATED'); """); // 方式1:验证数据库中存在数据 try (Connection conn = ConnectionManager.open()) { ResultSet rs = conn.prepareStatement("SELECT action FROM \"Client_Audit\" WHERE client_login = 'test_login'") .executeQuery(); assertTrue(rs.next()); assertEquals("BALANCE_UPDATED", rs.getString("action")); } // 方式2:捕获控制台输出验证(可选) // ByteArrayOutputStream out = new ByteArrayOutputStream(); // System.setOut(new PrintStream(out)); // auditService.printAudit("test_login"); // assertEquals("BALANCE_UPDATED\n", out.toString()); // System.setOut(System.out); } }
关键说明
@Testcontainers:启用Testcontainers与JUnit 5的集成,自动管理容器生命周期。@Container:标记的容器实例会在测试类启动时启动,测试结束后停止,复用容器提升效率。@BeforeEach:每次测试前初始化表结构和测试数据,保证测试用例之间的隔离性。- 测试用例中直接查询数据库验证操作结果,确保业务方法的正确性。
内容的提问来源于stack exchange,提问作者elSasen
相关产品推荐
相关产品推荐

