测试时不要欺骗自己
《纽约时报》不久前进行了一次测验,任务很简单:找出数字序列背后的数学规则。
作为读者,您可以输入自己的数字,看看它们是否符合或不符合您对规则的猜测。
在我看来,这和我们单元测试的方式非常相似。提供输入,验证输出。在这里,你提供输入(数字),输出是数字序列是否符合规则。
你可以想象一下它的单元测试:
function checkRule (numbers) {
// some magical formula we’re supposed to figure out
};
describe('my mathematical equation', function () {
it('should pass when doubling each number', function () {
expect(checkRule([1,2,4])).toBeTrue;
expect(checkRule([2,4,8])).toBeTrue;
expect(checkRule([3,6,12])).toBeTrue;
expect(checkRule([5,10,20])).toBeTrue;
});
})
看看这段代码,很容易假设规则是“每个数字都应该是前一个数字的两倍”。毕竟,我们的四个断言都通过了,所以我们得到了绿色测试!
这个测验的诀窍在于数学方程非常简单:每个数字都必须大于前一个数字。
这条宽泛的规则意味着人们很容易假设他们的复杂解决方案是正确的。他们为了验证规则而输入的每个值都返回 true,所以它一定是正确的。
然而,正如文章指出的那样,这种测试方法存在缺陷:
值得注意的是,到目前为止,玩过这个游戏的人中,有78%的人在没有听到任何“不”的情况下就猜出了答案。只有9%的人听到了至少三个“不”——尽管听到“不”并不会有什么惩罚或代价,除了每个人听到“不”时都会有的那种小小的失望。
文章将此归因于“确认偏差”,这部分适用。但更好的描述是一种鲜为人知的偏差,叫做一致性偏差。(在听到《怀疑论者指南》上的这篇文章之前,我并不知道这种偏差。)
这种偏见是“倾向于仅通过直接测试来检验假设,而不是对可能的其他假设进行测试”。
在我们上面的测试中,我们只检查肯定的结果。我们从来没有问过“如果我提供的数据与规则相矛盾,这是否会失败?”
每套单元测试都应该有否定检查。一个简单的否定expect(passesRule([2,4,6])).toNotBeTrue;
检查会破坏我们的测试,尽管 6 不是 4 的两倍,但规则仍然通过。
再次引用文章内容:
当你想要检验一个理论时,不要只寻找能证明它的例子。当你考虑一个计划时,要仔细思考它可能出错的地方。
第二部分对于测试来说尤其正确。
很容易想当然地认为,只要测试通过了,代码和测试就按预期工作了。但我们必须记住Edsger Dijkstra 很久以前说过的话:
测试表明了错误的存在,而不是不存在。
下次测试代码时,请考虑确认和一致性偏差。
记住“快速失败”这句话。证明你的代码确实如其所述,并在编码时始终保持怀疑的心态。
不要等到太晚才知道残酷的真相。
用理查德·费曼的话来说:
第一个原则是,你不能欺骗自己——而你是最容易被欺骗的人。
要对测验本身进行更多分析以及对一致性偏差的看法,请查看以下讨论:
标题照片由Hans-Peter Gauster在Unsplash上拍摄
文章来源:https://dev.to/klamping/stop-lying-to-yourself-when-testing-42bk