我读过肯特Beck关于Saff Squeeze方法的原始博客post,也读过this InfoQ post,它详细阐述了这个主题,但没有提供任何示例。
我知道这本质上是一种不依赖调试器就能找到bug的方法,但是我发现肯特的例子并不那么清楚。
有没有更开明的人可以用一个清晰、具体的例子来教我如何使用这种方法?它也有望成为其他研究这种方法的人的学习资源。
我读过肯特Beck关于Saff Squeeze方法的原始博客post,也读过this InfoQ post,它详细阐述了这个主题,但没有提供任何示例。
我知道这本质上是一种不依赖调试器就能找到bug的方法,但是我发现肯特的例子并不那么清楚。
有没有更开明的人可以用一个清晰、具体的例子来教我如何使用这种方法?它也有望成为其他研究这种方法的人的学习资源。
3条答案
按热度按时间bvhaajcl1#
Saff Squeeze是一种系统技术,用于从失败的测试中删除测试代码和非测试代码,直到测试和代码小到足以理解。
我同意Kent's original description of the Saff Squeeze有点困难,部分原因是他正在测试的软件JUnit是高度抽象的,部分原因是他没有给出足够的步骤2的示例,"在测试中比现有Assert更早地放置(失败的)Assert。"
在他的第一轮测试中,他只是将Assert移到了测试中的更高位置,他对后续步骤的总结可能会让您认为在第2步中唯一可以做的事情就是移动现有的Assert,但是到了最后一步,他已经提出了一个新的、更简单的失败Assert,第2步中的Assert可以只是一个在测试中移到更高位置的现有Assert,这是很常见的。但它也可以是随着您对代码和bug的理解的发展而产生的新问题。
这里有一个例子。它太简单了,无法
need
Saff Squeeze,但它说明了该技术。我刚刚写了这个任务关键型类:
下面是我为测试它而编写的第一个rspec示例:
糟糕--最后一行失败,显示以下消息:"期望落空:预期使用FlightPlan调用Autopilot. carry_out(纬度:1、经度:1),但它是用FlightPlan调用的(纬度:2、经度:2)"我不知道这是怎么发生的,我们最好用沙夫挤压法。
内联方法(重命名局部变量以避免名称冲突):
我看不出最后一行怎么会不符合预期,只要它得到了正确的
FlightPlan
,让我们看看是否可以在测试中更高的位置写一个失败的Assert:啊,新的Assert也失败了,"预期的飞行计划(纬度:1、经度:1),但得到了FlightPlan(纬度:2、经度:2)"好吧,让我们简化测试:
我们正在取得一些进展,但我仍然不知道哪里出了问题。再次使用Better Saff Squeeze,内联
flight_plan_to
:很明显,只要
flight_plan_to
得到正确的Runway,这种情况就会过去。很好,新Assert失败,"预期跑道(id:1)却得到了天桥骄子(id:2)".再次简化测试:
我们已经将原来的测试和代码精简到了
closest_available
中存在bug的程度--应该使用first
而不是last
。但是如果它仍然不明显呢?好吧,让我们再次尝试Saff Squeeze,内联
closest_available
:现在,我应该把一个失败的Assert放在测试中更高的位置吗?我不能--bug在测试的最后一行。最终我将被迫意识到,在我内联它之前,它在
closest_available
中。t5fffqht2#
样本显示他正在复制(inlining)将被测代码内嵌到单元测试中。然后从开始到结束分别测试代码的各个部分。这使他能够隔离测试每个路径,并在最小可能的单元上进行生产单元测试。其中一个测试将演示缺陷,您将能够修复缺陷。他所展示的示例取决于Eclipse内联方法的能力,如果您不具备这种能力,则需要手动执行(将调用的代码复制到单元测试中)。
s1ag04yj3#
这实质上是一个代码树的深度优先搜索,通过遍历单元测试中的代码树来找到bug,然后修剪掉任何不包含bug的分支。
我怀疑,如果代码包含导致观察到的(错误)行为的多个问题,那么这可能会有问题。
如果你在代码树中得到一个有大量子分支的a节点(一个很长的方法),我更喜欢使用启发式二分搜索来识别bug所在的位置:)