问题背景
手动检查SQL中的业务逻辑(在存储过程、函数、触发器级别)会导致错误,这些错误只会在生产环境中暴露。长期以来,SQL脚本的测试是不正式和非标准化的。然而,CI/CD技术的发展要求对SQL代码进行自动化测试。
问题
大多数开发人员仅限于应用级别的测试。对SQL过程和函数缺乏检查会导致缺陷,这些缺陷不会被任何测试包覆盖——例如,当UDF逻辑更改或报告发生回归时。
解决方案
在现代团队的工作流程中,直接为SQL代码组织单元和集成测试。单元测试使用tSQLt(SQL Server)、utPLSQL(Oracle)、pgTAP(PostgreSQL)等框架,而集成测试则使用单独的环境来启用临时数据库、应用迁移和检查业务场景。
pgTAP的单元测试示例:
-- 检查薪资分离 SELECT plan(2); SELECT is( (SELECT calc_salary(1)), 1000, '用户1的薪资正确' ); SELECT isnt( (SELECT calc_salary(2)), 0, '用户2的薪资不为零' ); SELECT finish();
CI/CD的集成测试代码:
psql -U user -d testdb < migrations.sql psql -U user -d testdb < test_data.sql psql -U user -d testdb -c "SELECT * FROM my_procedure_test();"
关键特点:
如果过程很大,能否仅依赖应用层的自动测试? 不能,因为UI/API测试不能保证SQL逻辑的正确性(例如,存储函数内部的条件错误或数据更新时的违反)。单元测试应覆盖SQL代码中的所有根分支执行。
仅仅在数据库中有少量变更,依靠“手动”运行测试脚本是否足够? 不够,即使在小型项目中,变更架构或逻辑后也会出现bug。CI过程中自动化测试可以减少人为因素,防止回归。
是否可以只测试“关键”过程,而忽略其他? 最佳的方法是尽可能覆盖更多的函数,特别是当未来代码将由多个团队修改时:不明显的计算和边缘情况往往在不标准的分支中表现出来。
在完善折扣计算过程时,手动测试了几个案例,但遗漏了主要逻辑分支。生产环境中客户开始获得错误的折扣,需要几天时间进行调查。
优点:
开始时节省时间。
缺点:
手动修正的损失,修改和重构时的不便。
使用pgTAP为所有关键的UDF和过程开发单元测试,集成测试通过CI在每次合并分支时运行。错误和回归在部署之前被发现。
优点:
功能稳定,能够快速完善业务逻辑,生产环境中的bug最小。
缺点:
启动和维护测试数据库需要投入时间。