基于属性的测试:框架抽象边界与生成属性融合难题 【导语本文围绕基于属性的测试PBT展开探讨了其属性与生成器的关系指出在实际应用中两者并非相互独立还分析了不同 PBT 框架在处理抽象边界时的表现及存在的问题。】属性与生成器PBT 的核心要素在基于属性的测试PBT中属性是一种全称量化的计算必须对所有可能的输入都成立在编程语言中最简单的模型是一个返回布尔值的函数。例如 \l - reverse (reverse l) l 断言对列表进行两次反转后会得到原始列表。当涉及前置条件时情况会更复杂如 prop_insert_select 函数需检查数据库中是否存在指定的表。除了属性还需要为其准备随机生成器。可以为所有输入类型编写 Arbitrary 实例但简单随机生成可能导致前置条件失败因此需要依赖生成器让一些值依赖于其他值确保输入在构造时就是有效的。生成与属性的交织打破传统边界生成器与属性并非相互独立生成器执行的计算可能与随机生成本身无关。如在处理 Database 时因太复杂无法从头生成需先生成简单版本再用其 API 构建。同时可将属性的一部分移到生成器中让属性变为全函数生成器变为部分函数。QuickCheck 支持基于属性的测试编写方式通过 forAll 组合器接受生成器创建可访问生成值的上下文使属性成为包含生成和断言的测试。这表明基于属性的测试是属性和生成器的结合很多时候不打破抽象边界就无法使用这些属性。不同框架的表现抽象边界处理差异不同的 PBT 框架在处理抽象边界时表现不同。Haskell 的 QuickCheck 的 Rust 移植版 quickcheck 没有处理好抽象边界其 quicktest 期望一个 Testable 实例设计假设了不充分的边界计算可能需要与生成过程交织在一起。Proptest 也做出了类似决定。新推出的 [Hegel](https://github.com/hegeldev/hegel-rust) 保留了 tc: TestCase 参数允许将生成与测试用例混合通过不同机制实现了 Haskell 中的原始功能而 Hypothesis 早已使用这种机制。编辑观点基于属性的测试在软件开发中具有重要意义但目前各框架在处理属性与生成器关系及抽象边界上存在差异。开发者需根据具体需求选择合适框架未来框架应更好融合生成与属性提升可表达性。