靠谱,但需手动定制生成器、严谨定义性质并限制CI参数。RapidCheck成熟可用,Autocheck已停更;须重载生成器避免脏数据,property需规避浮点误差与全局状态,CI中应固定seed、减少次数并禁用shrink。
靠。但不是“开箱即用”。RapidCheck 和 Autocheck 都是 C++ 的 property-based testing(PBT)库,其中 RapidCheck 更成熟、文档更全、社区更活跃;Autocheck 已基本停止维护(最后提交在 2018 年),不建议新项目选用。
PBT 失败往往不是因为断言错,而是生成的数据太“脏”——比如传入空指针、负数长度、非法 UTF-8 字节序列,导致被测函数提前崩溃,根本没走到你要验证的性质(property)上。
rc::gen::arbitrary() 做起点,但**必须重载或组合生成器**:例如对 std::string,默认生成含嵌入 null、控制字符、超长字符串;应改用 rc::gen::string(rc::gen::alpha()) 限定字符集rc::Gen 特化,不能依赖 ADL 或隐式构造——否则会 fallback 到 bit-wise 随机填充,大概率触发 UBrc::gen::filter() + 简单生成器,否则 shrink 会失效或极慢auto genValidUrl = rc::gen::filter([](const std::string& s) {
return !s.empty() && s.find("://") != std::string::npos;
}, rc::gen::string());常见原因是 property 描述不严谨,或忽略了浮点、并发、时序等非确定性因素。
assert(x + y == y + x) 对 float 不成立——应改用近似相等:std::abs(x + y - (y + x))
std::time(nullptr))、随机数(std::rand()),每次运行行为不同,shrink 会失败,RapidCheck 可能报 Failed to shrink 或直接跳过is_sorted(output) 和 multiset_equal(input, output)
默认情况下,RapidCheck 每个 property 运行 100 次用例,且默认开启 shrink(可能额外跑数百次)。CI 中应严格限制:
rc::prop::forAll(...).run({.tests = 50, .seed = 42}) 固定 seed 和次数,避免 flaky.run({.shrink = false}) ——定位 bug 是开发机的事,CI 只需快速反馈是否“当前代码通过基础性质”test_pbt 可执行文件,CI 中用 timeout 60 ./test_pbt 控制最长耗时真正难的是设计好 property:它得足够强以捕获 bug,又不能太强而误报。这点没有捷径,只能从最朴素的不变量开始——比如“parse + serialize 应等于原输入”,再逐步叠加约束。