举例来说,Chrome在高端的桌面电脑上与Chrome移动版在智能手机上的表现就大相径庭。而一个充满电的智能手机与一个只剩2%电量,设备开始降低无线电和处理器的能源供应的智能手机的表现也完全不同。

    如果在横跨多于一种环境的情况下,你想在任何合理的意义上宣称“X比Y快”,那么你就需要实际测试尽可能多的真实世界的环境。只因为Chrome执行某种X操作比Y快并不意味着所有的浏览器都是这样。而且你还可能想要根据你的用户的人口统计交叉参照多种浏览器测试运行的结果。

    有一个为此目的而生的牛X网站,称为jsPerf( )。它使用我们前面提到的Benchmark.js库来运行统计上正确且可靠的测试,并且可以让测试运行在一个你可交给其他人的公开URL上。

    每当一个测试运行后,其结果都被收集并与这个测试一起保存,同时累积的测试结果将在网页上被绘制成图供所有人阅览。

    当在这个网站上创建测试时,你一开始有两个测试用例可以填写,但你可以根据需要添加任意多个。你还可以建立在每次测试轮回开始时运行的代码,和在每次测试轮回结束前运行的teardown代码。

    注意: 一个只做一个测试用例(如果你只对一个方案进行基准分析而不是相互对照)的技巧是,在第一次创建时使用输入框的占位提示文本填写第二个测试输入框,之后编辑这个测试并将第二个测试留为空白,这样它就会被删除。你可以稍后添加更多测试用例。

    你可以顶一个页面的初始配置(引入库文件,定义工具函数,声明变量,等等)。如有需要这里也有选项可以定义setup和teardow行为——参照前面关于Benchmark.js的讨论中的“Setup/Teardown”一节。

    jsPerf是一个奇妙的资源,但它上面有许多公开的糟糕测试,当你分析它们时会发现,由于在本章目前为止罗列的各种原因,它们有很大的漏洞或者是伪命题。

    关于这个测试场景有一些现象值得我们深思:

    这里有另一个展示苹果比橘子的常见漏洞的例子:

    这里,明显的意图是要找出自定义的mySort(..)比较器比内建的默认比较器慢多少。但是通过将函数mySort(..)作为内联的函数表达式生命,你就创建了一个不合理的/伪命题的测试。这里,第二个测试用例不仅测试用户自定义的JS函数,而且它还测试为每一个迭代创建一个新的函数表达式。

    不知这会不会吓到你,如果你运行一个相似的测试,但是将它更改为比较内联函数表达式与预先声明的函数,内联函数表达式的创建可能要慢2%到20%!

    基于前一个例子,另一种造成苹果比橘子场景的陷阱是,不透明地对一个测试用例回避或添加“额外的工作”:

    将先前提到的内联函数表达式陷阱放在一边不谈,第二个用例的mySort(..)可以在这里工作是因为你给它提供了一组数字,而在字符串的情况下肯定会失败。第一个用例不会扔出错误,但是它的实际行为将会不同而且会有不同的结果!这应当很明显,但是:两个测试用例之间结果的不同,几乎可以否定了整个测试的合法性!

    但是除了结果的不同,在这个用例中,内建的sort(..)比较器实际上要比mySort()做了更多“额外的工作”,内建的比较器将被比较的值转换为字符串,然后进行字典顺序的比较。这样第一个代码段的结果为[-14, 0, 0, 12, 18, 2.9, 3]而第二段代码的结果为[-14, 0, 0, 2.9, 3, 12, 18](就测试的意图来讲可能更准确)。

    所以这个测试是不合理的,因为它的两个测试用例实际上没有做相同的任务。你得到的任何结果都将是伪命题。

    这些同样的陷阱可以微妙的多:

    这里的意图可能是要测试如果x表达式不是Boolean的情况下,? :操作符将要进行的Boolean转换对性能的影响(参见本系列的 类型与文法)。那么,根据在第二个用例中将会有额外的工作进行转换的事实,你看起来没问题。

    微妙的问题呢?你在第一个测试用例中设定了的值,而没在另一个中设置,那么你实际上在第一个用例中做了在第二个用例中没做的工作。为了消灭任何潜在的扭曲(尽管很微小),可以这样:

    现在两个用例都有一个赋值了,这样你想要测试的东西——x的转换或者不转换——会更加正确的被隔离并测试。