提问人:Opack 提问时间:11/7/2023 更新时间:11/8/2023 访问量:33
为什么我应该在测试中使用三角测量而不是随机值?
Why should I use triangulation instead of just randomized values in my tests?
问:
一点背景
你好!
自从我开始学习和使用 TDD 以来(不久前😅),我就对“三角测量技术”感到好奇:当我们可以编写一个使用随机值的测试时,为什么还要编写多个测试呢?
今天,我偶然发现了这篇文章,它解释了“三角测量技术”。然后我向作者提出了一个关于这个“随机化而不是三角测量”的问题。 我对他对此的看法非常感兴趣,但我也想就此事收集尽可能多的观点。所以这是我的问题。
推理
他的解释非常有趣,因为我更喜欢随机值,因为我认为使用一个或两个甚至三个不同的值并不能“证明”我的系统是健壮的。但他对重构阶段的见解是关键,这也应该避免测试和实现之间的重复。
话虽如此,我仍然觉得它有点没😅用,我的意思是:使用这种技术,必须编写多个类似的测试(至少 2 个),一个会不断发展的实现,然后对所有这些进行重构以删除除一个之外的所有测试。 剩下的测试不会表达最终解决方案的复杂性,因为它与实现之间没有直接联系。它不认可其“记录”实施的作用。它不能用于再次导致实现。
另一方面,对于随机值,我必须直接调整我的实现,以适应该值未知的事实。我不能使用简单的常量来使测试通过。我不必编写多个将被删除的测试。我不必足够严谨,也不会忘记做这个测试实现重复数据消除重构。我的测试确实表达了实现所满足的正确需求(即:给定“任何”两个数字,我返回总和)。
问题
那么,根据你的经验,你认为三角测量比使用随机值有什么优势吗?
感谢您的见解!🙏🏽
答:
这与三角测量与基于属性的测试无关!这是关于如何同时使用两者,问题是本文使用了一个非常微不足道的例子,其中基于属性的测试从一开始就 100% 有意义。
在构建解决方案时使用三角测量,以便一次执行一个步骤。如果您从事的技术具有良好的 REPL(Haskell、Clojure 等),则可以直接从 REPL 完成。这有助于建立信心,即该解决方案适用于我们抛出的基本方案。
解决方案完成后,编写一个(或多个)基于属性的测试,这些测试将涵盖多个其他基本测试。
测试也应被视为被测系统 (SUT) 工作方式的文档。在某些情况下,最好进行一些非基于属性的测试来描述 SUT 的工作原理。还有一个关于创建随机值的代码复杂性的迷你警告,如果太复杂,它有可能使测试变得非常难以理解。
当肯特·贝克(Kent Beck)首次引入三角测量比喻时,他写道:
我尝试过三角测量,但这些步骤对我来说似乎没有价值,所以我 很快失去动力。
那么,根据你的经验,你认为三角测量比使用随机值有什么优势吗?
这是一个有点复杂的问题。
有一些 TDD 的在线测试演示。
然而,当我尝试它时,我很快就遇到了几个问题
除非您尝试解决一个微不足道的问题,否则定义属性是很困难的。
目前尚不清楚测试是否是用于确保属性成立的正确工具。例如,Jim Coplien 写道,大多数单元测试应该是断言。
这里的部分紧张关系是,虽然属性驱动的测试可能更适合测试,但 TDD 的意图是在分析和设计领域。
我为有效的代码获得报酬,而不是为测试而获得报酬,所以我的理念是尽可能少地测试以达到给定的置信度水平——Beck,2008
我们在 TDD 中创建的检查是由开发人员为开发人员创建的。因此,除非使用随机数据进行测试,否则提供其他方式无法获得的设计见解,否则戴上“错误”帽子似乎是优先事项。
经过几十年的TDD,我早就得出结论,没有一种技术可以解决所有问题。正如陈词滥调所说,使用正确的工具来完成工作。
有时带有三角测量的 TDD 效果很好,但在其他情况下,它确实效果不佳。下面是一个示例,其中示例驱动的开发效果不佳,但基于属性的测试效果很好: 当属性比示例更容易时
本页上的其他人表示,基于属性的测试可能适用于简单的问题,但可能不适用于“真正的”问题。我不敢苟同。上面的链接是一个例子,在这个例子中,基于属性的测试使我摆脱了无法轻易通过三角测量的方式摆脱了这种情况。
另一个更简单的问题,基于属性的测试比三角测量更适合,是 Diamond kata。至少,这是我的经验,我试图以两种方式解决它。
为什么我应该在测试中使用三角测量而不是随机值?
你不应该。有时三角测量是最好的方法,有时,其他技术(如基于属性的测试)效果更好。
有时您可以组合方法。例如,在实现被测系统之前,您仍然可以编写属性,因此这仍然是 TDD。您也可以将其称为三角测量,但比例更精细,因为单个属性通常会使用数百个随机生成的值运行。
下面是结合 TDD 和 PBT 的示例: 基于属性的测试与分区测试不同
剩下的测试不会表达最终解决方案的复杂性,因为它与实现之间没有直接联系。它不认可其“记录”实施的作用。它不能用于再次导致实现。
也许吧,但实施不是目标。事实上,有人可能会争辩说,实现是一个实现细节。重要的是,自动化测试执行被测系统 (SUT) 的行为。
如果假设删除所有 SUT 代码并从测试套件中重新实现它,那么新代码看起来与旧代码不同是否重要?
也许新代码更好。这就是重构背后的基本思想:在不改变系统行为的情况下改变代码的结构。
评论