基于 属性 的测试的不确定性是否会影响构建的可重复性?

Does non-deterministic nature of property-based testing hurt build repeatability?

我正在学习 FP,并了解了基于 属性 的测试的概念,对于来自 OOP 世界的人来说,PBT 看起来既有用又危险。它确实检查了很多选项,但是如果有一个(或一些)选项失败了,但它们在您第一次让我们说 Jenkins 构建期间没有失败怎么办。那么下次你 运行 构建测试可能会或可能不会失败,它不会扼杀可重复构建的整个想法吗?

我看到有些人 explored options to make the tests deterministic,但是如果这样的测试没有发现错误,它就永远不会发现错误。

那么这里有什么更好的方法呢?我们是牺牲构建可重复性以最终发现错误,还是冒着永远不会发现错误的风险,但又恢复了可重复性?

(我希望我正确理解了 PBT 的概念,但如果我没有理解,如果有人能指出我的误解,我将不胜感激)

做了很多基于 属性 的测试,我不认为不确定性是个大问题。我基本上经历了三种:

  1. A 属性 确实是不确定的 b/c 某些外部因素 - 例如超时、延迟、数据库配置 - 使之成为现实。这些不稳定的测试也会出现在基于示例的测试中,应该通过确定外部因素来消除它们。

  2. A 属性 很少失败,因为触发条件只是有时通过伪随机数据生成来满足。大多数 PBT 库都有方法重现那些失败的 运行s,例如通过重新使用失败测试的随机种子 运行 或者甚至记住某种数据库中的确切星座。这些失败揭示了问题,也是我们首先进行随机测试用例生成的原因之一。

  3. 覆盖断言(“在所有情况下至少有 5% 会出现此条件”)可能会不时失败,即使它们通常是正确的。这可以通过增加尝试次数来缓解。一些库,例如 quickcheck,自己计算 prove/disprove 覆盖假设需要多少次尝试,从而大部分消除那些误报。

重要的是始终跟进不稳定的故障并找到错误、不确定的外部因素或 属性 的不变量中的错误假设。当您这样做时,偶发故障的发生频率就会越来越低。我的个人经历主要是在 jqwik 上,但其他人也告诉过我类似的故事。

非确定性是正确的。

有一件事我想补充一点:非确定性不仅是一种罕见的小成本,而且还是有益的。如果您的测试套件的第一个 运行 成功,坚持确定性意味着坚持未来 运行s(针对同一系统的同一套件)将发现零错误。

通常大多数测试套件包含许多独立系统部分的独立测试,并且提交很少更改系统的大部分。因此,即使跨提交,大多数测试在前后测试完全相同的东西,确定性再次保证你会发现零错误。

允许随机性意味着每个 运行 至少有机会发现错误。

这当然提出了回归测试的问题。我认为标准的论点是这样的:为了使每项工作的价值最大化,您应该将测试重点放在代码中最容易出错的部分。过去观察到一个错误可以提供关于代码的哪一部分有错误(以及它可能有哪种错误)的证据。您应该使用该证据来指导您的测试工作。 (通常像激光一样专注于一个具体的错误。)

我觉得这个说法很有道理。我还认为有不止一种方法可以很好地利用错误提供的证据。

例如,您可以编写一个生成器,生成与第一次触发错误的数据相同类型和形状的数据,and/or 是为触发错误而量身定制的。

And/or,您可能想要编写测试来专门验证那些被错误行为违反的属性。

如果您想判断这些测试的好坏,我建议 运行 对它们进行几次测试(在正常大小的输入批次上)。如果他们每次都触发该错误,那么将来也可能会触发该错误。

这是一个(希望深思熟虑的)发人深省的问题:发布之前有错误的软件和发布有新错误的软件哪个更糟?换句话说:捕获过去的错误是否比捕获新错误更重要——还是这样做主要是因为它更容易?

如果你认为我们这样做的部分原因是它更容易,那么我认为重新捕获错误是有概率的并不重要:你真正应该关心的是平均捕获错误的能力属性 测试——它在其他地方的好处应该超过旧错误通过的相当小的机会,即使在您评估回归测试时它在(比如说)5 个连续的 运行 测试中被发现。

现在,如果你不能可靠地生成触发错误的随机输入,即使你很好地理解错误,或者执行它的生成器又大又复杂,因此维护成本高,请手动选择回归示例似乎是一个完全合理的选择。

通过在构建过程之外生成随机性,您可以同时拥有非确定性和可重现的构建。您可以在开发期间或外部测试期间生成它。

一个示例是为基于 属性 的测试播种,并在提交时自动修改此种子。你还在做权衡。开发人员可能会收到与他们正在处理的内容无关的错误的警报,并且您会失去一些测试能力,因为测试更改的频率可能较低。

您可以通过减少种子更改的频率来进一步向确定性方向倾斜权衡。例如,您可以为每个程序组件或文件设置一个种子,并且仅在提交相关文件时更改它。

另一种方法是在开发过程中根本不更改种子。相反,您可以让自动 QA 使用随机种子进行定期或连续测试,并使用它们来生成可以在方便时处理的错误 reports/issues。