如果我已经练习了基于示例的测试,为什么还要真正使用基于 属性 的测试?

Why should I really use Property-based testing if I already practice Example-based testing?

一些开发人员就 TDD 与基于示例的测试争论不休的一个警告是,可能缺少每个有效的输入处理。

让我们举一个简单的例子,那些开发人员可能会争论:

Make a program that add two numbers.

Bob,开发人员开始编写第一个测试:
给定1+3,则结果为4.
实现:def add(x: Int, y: Int) = 4
不错,过了。

现在第二次测试:
给定 2 + 2,则结果为 4。 实现仍然相同:def add(x: Int, y: Int) = 4

所以其他开发人员会过来告诉他:

“现在你看到 Bob 说基于示例的测试对于你可怜的 2 个示例是不够的!
您没有测试每个输出并查看您的实际实现,对于结果不同于 4 的其他输入,它将失败!
现在,听我们说,开始编写一些基于 属性 的测试来覆盖所有有效输入。
因此,让我们从特定于加法的交换性测试、结合性等开始:加法的 属性 !”

但是但是但是...Bob 的TDD 实践真的很糟糕!
事实上,他想应用 三角测量,但他写了一个测试已经成功,因为实现没有被改变!

要进行三角剖分,应该编写一个失败的测试。 而三角剖分是TDD实践的万能钥匙之一。
它允许主要步骤:重构,因为您应该管理导致 2 个不同结果的两条路径!

=> 只要测试变得具体,由于重构,代码就会变得更加通用。

所以我的问题是:
如果我们通过三角测量的良好实践来实践严格的 TDD,那么使用基于 属性 的测试的真正好处是什么,断言 99% 的情况下的不变量已经被良好的 TDD 覆盖?
事实上,假设开发人员具有良好的智商并构建有意义的实现 ;)

我的例子来自those slides.

属性 当边缘案例很难找到或者边缘案例太多以至于程序员很容易错过一个时,基于

属性 的测试非常有用。例如,我在实现 hirschberg 算法时使用了它。没有明显的方法可以将算法划分为更小、琐碎、易于 TDD 测试的部分。并且很难手工制作涵盖所有可能算法路径的输入。最好的方法是生成大量不同的输入以覆盖所有路径。当自动检查发现错误时,将特定输入添加到回归测试中

如果您练习 TDD,您就会知道这是一种思考和进行设计而不是测试的方式。

TDD 起源于增量的、主要是基于状态的单元测试。然后 交互风格(即,mockist 风格,或伦敦风格)TDD 改变了我们的方式思考代码设计代码。

属性-based TDD 也有机会改变我们设计代码的方式。我们没有生成示例来推动我们的设计前进,而是使用属性。这导致更少的测试,更易于维护,测试覆盖率更高。

另一方面,它比基于示例的 TDD 更难,需要更多思考。