内容提要
本文介绍PostgreSQL场景树测试模式:将业务分支场景表示为目录树,利用保存点(savepoint)共享历史状态,深度优先遍历各分支,在未提交的部署事务中运行测试,仅当所有场景通过才提交迁移。该方法避免重复构建共享前缀状态,能有效测试历史依赖型缺陷,并作为部署提交门禁。
延伸解读
共享历史,而非共享状态
场景树的核心在于:兄弟分支共享的是建立好的历史状态,而不是彼此的事务变更。通过保存点回滚,每个分支在继承父状态的同时,不会看到兄弟分支的修改。这解决了测试污染问题,同时避免了重复构建共享前缀的开销。
部署提交门禁的实践要点
将场景树运行在未提交的部署事务中,只有所有场景通过才提交迁移。但需注意:迁移锁会持有到提交,增加锁等待时间;必须确保异常能传播出树,否则静默失败会导致错误提交;空树应视为失败。建议使用事务级锁超时,并持久化测试结果。
场景树的边界与局限
场景树无法覆盖并发、跨事务边界、时间依赖和外部副作用。它只验证单会话内的历史依赖缺陷,且是作者选择的有限分支,而非穷尽验证。序列号不回滚、回滚非物理等特性也需注意。适合作为补充手段,而非替代其他测试。
Q&A
什么是PostgreSQL中的场景树测试?
场景树测试是一种将业务逻辑的分支场景表示为目录树,并使用保存点(savepoint)在事务内共享历史状态、深度优先遍历各分支的测试方法。它避免了重复构建共享前缀状态,并可作为部署提交的门禁。
场景树测试如何利用保存点共享历史状态?
在场景树中,每个目录代表一个业务状态,其_setup.sql应用转换。测试运行时,使用保存点标记每个分支点,执行一个分支后回滚到保存点,再执行兄弟分支,从而共享已建立的历史状态,而不会看到兄弟分支的更改。
场景树测试如何作为部署提交门禁?
由于PostgreSQL的大多数DDL是事务性的,可以在未提交的部署事务中应用迁移,然后运行场景树测试。如果所有场景通过,则提交迁移;否则回滚整个事务,从而防止有缺陷的迁移成为数据库的schema。
场景树测试相比传统测试方法有什么优势?
传统测试方法为每个场景重建共享前缀状态,导致重复工作。场景树测试通过保存点共享历史,只执行一次共享前缀,然后分支测试,节省了设置成本,尤其对于深度场景,同时确保每个分支从正确的父状态开始。
场景树测试有哪些局限性?
局限性包括:只能测试单会话内的顺序历史,无法测试并发问题;无法测试提交时行为(如NOTIFY);now()在事务内不变,无法测试时间依赖逻辑;序列值不回滚;回滚是逻辑的,物理写入仍存在;树结构无法处理循环或合并分支;串行执行,无法并行。
如何编写场景树测试的目录结构?
目录结构以__test__/为根,每个子目录代表一个业务状态。根目录的_setup.sql建立初始状态,非根目录的_setup.sql应用转换。每个目录中的其他.sql文件是断言。测试运行器按深度优先遍历,先执行转换,再执行断言,然后处理子目录。
场景树测试中如何确保兄弟分支的隔离?
通过保存点回滚,每个分支的更改在下一个兄弟分支开始前被回滚,因此兄弟分支只共享已建立的历史状态,而不会看到彼此的更改。这保证了测试的独立性。
场景树测试适合测试哪些类型的缺陷?
场景树测试特别适合测试历史依赖型缺陷,即只有在特定序列的先前转换建立状态后才会出现的缺陷。它通过从正确的父状态执行每个分支来验证路径正确性,而不是仅仅测试单个操作。