无框架旧Java项目中含SQL查询方法的JUnit测试最佳实践咨询
老旧无框架Java项目DAO层SQL方法单元测试方案
首先明确:内存数据库和事务回滚两种方案都可行,可根据你的测试需求选择
方案选择建议
- 如果需要验证SQL在生产使用的真实数据库上的兼容性,优先选事务回滚方案,不需要额外引入依赖,也不会产生垃圾数据
- 如果要做完全隔离、不依赖外部服务的纯单元测试,优先选内存数据库方案,测试执行速度更快,稳定性更高
方案1:事务回滚方案实现方式
你的SQL方法是外部传入Connection对象,没有内部处理事务,刚好可以用这个方案:
- 测试代码中获取开发数据库连接后,先关闭自动提交:
dbConnection.setAutoCommit(false); - 调用待测试的SQL方法,执行完后做结果断言
- 测试用例结束后主动执行回滚:
dbConnection.rollback();
所有测试产生的数据变更都会被撤销,不会写入开发库产生垃圾数据。
注意:如果你的SQL方法内部主动执行了
commit操作,该方案不适用。
方案2:内存数据库方案实现
推荐用轻量的H2内存数据库,兼容大部分主流关系型数据库的常用SQL语法。
疑问1:内存数据库的创建与配置
首先下载H2的jar包导入项目的测试依赖即可,不需要额外安装服务。
创建方式可根据测试规模选择:
- 测试用例少的情况:直接写在测试类的初始化方法中,测试类启动时执行建表、初始化基础测试数据的逻辑即可
- 测试用例多的情况:把连接参数、建表SQL、初始化数据SQL单独放到
test-config.properties配置文件中,测试代码读取配置加载即可,后续修改配置不需要改动测试逻辑
H2内存数据库的连接URL示例:jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1,表示创建名为testdb的内存实例,进程运行期间数据库不会自动销毁。
疑问2:连接传入方式
你的待测试方法第一个入参就是Connection对象,测试代码中直接通过H2驱动获取内存数据库的连接,传入方法即可,完全不需要修改现有的业务代码。
示例测试代码片段:
// 测试类初始化阶段执行 Class.forName("org.h2.Driver"); // 获取内存数据库连接 Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", ""); // 初始化表结构和测试数据 Statement stmt = conn.createStatement(); stmt.execute("CREATE TABLE person(id int primary key, age int)"); stmt.execute("INSERT INTO person(id, age) VALUES(1, 20)"); // 测试阶段直接传连接 sqlUpdateAge(conn, 30, 1); // 断言验证结果 ResultSet rs = stmt.executeQuery("SELECT age FROM person WHERE id = 1"); Assert.assertTrue(rs.next() && rs.getInt("age") == 30);
额外最佳实践建议
- 无论用哪种方案,每个测试用例执行前都要重置测试数据,避免不同用例之间的执行结果互相影响
- 如果使用H2内存数据库,注意测试你的SQL是否用到了生产数据库的独有语法,如果存在不兼容的情况,建议改用事务回滚方案测试真实数据库
- 你给出的示例代码中直接捕获所有异常仅打印堆栈,建议测试时可以补充异常抛出的校验,或者调整业务代码把异常向上抛出,方便测试定位问题
内容的提问来源于stack exchange,提问作者willberthos
相关产品推荐
相关产品推荐

