一个你一眼看不出来的Bug

648 查看

又一个你一眼看不出来的Bug

a = [3,2,4]
a.sort(function(x,y){return x>y})
//上面的代码有问题,你信吗;可是下面的测是过的

expect(a).to.deep.equal([2,3,4]) // ok

似乎有了单元测试也不能帮助我们发现问题,在试试这样的测试.

var b= [1,2,10,3,78,8,90,11,3,20,7,4,2]
b.sort(function(x,y){return x>y})

expect(b).to.deep.equal([ 1, 2, 2, 3, 3, 4, 7, 8, 10, 11, 20, 78, 90 ]) // not ok
// b = [ 90,1,11,2,2,3,3,4,7,8,10,20,78]

为什么

代码说明一切 https://github.com/v8/v8/blob/5.2.176/src%2Fjs%2Farray.js#L808
当数组长度小于等于10的时候js的排序是采用的插入排序,插入排序只关心比较函数告诉它x不是比y大,即函数的返回值是不是大于零,如果大于零就交换;而当数组的长度大于10的时候,数组的sort方法就采用快速排序了,而快速排序需要知道xy是大于(1),等于(0),小于(-1)三种关系, 显然如果返回的是布尔值的话,只有1 (Number(true)) 和 零 Number(false), 那么快速排序就不能正确的移动元素的位置了.
那要修复这样错误只要>变成-号就可以了.

真的有这样bug

之前做的应用,使用了mongodb的副本集,因为要机房切换,一次性在新机房创建了很多副本集,本以为这样会让系统中的每个节点的负载更小.但事实情况确实负载变得非常的不均衡,导致业务响应很慢.

查看mongodbnode.js驱动https://github.com/mongodb/node-mongodb-native/blob/V1.4.28/lib/mongodb/connection/repl_set/strategies/statistics_strategy.js#L77,发现在对副本集打分后排序的函数就犯了相同的错误.(现在这问题已经修复了.) 发现这样问题也是恰好我们的副本集的数量超过了10个,触发了这样的问题.

一点点思考

  • 使用了动态语言之后,对于语言的使用者应该有更高的要求,比如对常用的api的掌握.动态语言带来的自由和奔放,应该是是建立在使用者的自律和严谨的基础上的. 具体到node.js的技术上来说的话,也是这样的原因会让人觉得学习曲线略高和坑略多.

  • 如何通过测试发现这样的问题;显然如果你知道Array#sort的工作原理,就会在测试的时候把需要排序的数量提高到11.但是如果不知道呢? 比比如TDD的时候,我们测试用例是从0到1,再到多的一个模式.但是这个是多少呢? 这个就只能通过验收测试(accept testcase)来选择.单元测试只是为了保证你自己写的代码正确与否;集成测试为了保证你写的代码和那些你不能修改的代码在一起能否正常工作;只有系统测试才能保证你的产品是符合不符合你的需求的.作为开发人员测试的角度不要仅仅停留在单元测试层面,换一个角度会有一个新的质量保护网.

如果本文你对有用 欢迎请我喝咖啡