视频与幻灯片(含视频下载): http://fronteers.nl/congres/2009 ... -Javascript-testing
演讲很口语化, 且没有抄本记录, 因此翻译的过程稍微删减了一点无法听清的内容与废话. 但是大意应该没有影响.
John Resig的演讲已经逐渐从JS测试与性能剖析转向了jQuery 1.4.x的宣传.
这一部是他唯一集中讲述测试的演讲, 也是我认为最值得分享的.
JavaScript的测试似乎很少有人重视的样子. 另一方面测试的实施也的确困难.
不过既然要把前端开发当作是专业独立的职业来做, 实施测试真的很有必要.
即使没有开发自己的库/框架, 测试也可以建立起重构的基础与代码质量的信心.
[中括号部分一般是我加入的注释]
--- 正文 ---
感谢大家. 也感谢主办方邀我来此. 今天我想谈一点JavaScript测试中的问题所在, 我个人重视JavaScript测试的原因 -- 我觉得其重要性甚至要超过服务器端程序的测试, 以及可行的实施测试的手段 -- 至少在我的经验中是可行的. 希望能给未曾尝试过JavaScript测试的童鞋们一点启示.
一个最常问及的问题就是: 为什么要做JavaScript测试. 也许从一个服务器端程序员的视角来看JavaScript不过是小儿科(而不必测试), 也许问话者根本不了解测试. 而实际情况是: 为了兼顾多种运行平台, 要想写出高质量的JavaScript代码, 很难. 我们得设法了解何处会碰到浏览器不兼容问题, 而测试可以帮我们侦测这类问题的所在.
我想强调的是JavaScript测试与服务器端/桌面应用程序的测试都有所不同. 在那两种环境中, 测试的主要性质是回归测试, 亦即对既有代码的修改不会破坏软件原有的功能. JavaScript测试也包含了这一方面, 但我们主要用它来侦测诡异的跨浏览器问题, 其效果比我们想象中要好得多.
当我们开始写客户端JavaScript代码时, 肯定先要做这么一个研究: 兼容各种浏览器的成本与收益对比. 一般来说收益就等于某种浏览器的市场占有率--有多少用户在用它. 而成本的意义则是我们在开发过程中要解决的浏览器bug. 当然, 各人面对的具体情况可能不同. 比如说你的网站访客跟美国网民的统计结果很可能不同, 面向全球的网站, 比如雅虎, 和你的网站情况也未必一致. 开发者具体要支持哪些浏览器, 只有靠你自己心理有数了.
Yahoo做出了选择并且把他们的浏览器支持表公布出来, 用于说明哪些浏览器他们会给予全力支持. 他们的QA试验室里有专门的电脑来测试表中所有标作A级的平台. 比如说Mac OS X 10.4上的Safari 3.2, 他们会真的弄一台这样的电脑, 然后抓一个人上去测试. 可能我们绝大多数人都没有这样的测试条件--毕竟这样消耗的人力物力真的很大--专门雇用员工来做测试, 我真的很嫉妒.
jQuery项目的做法与此不同. 我们的方案是支持主流浏览器的当前版本, 前一版, 和下一版. 我们持续关注浏览器在新版本中的变动, 研究是否要予以支持. 这个表格是我们1.3版本的支持范围. 下一版1.4版将会加入更多的浏览器支持, 大概二十到三十个版本这样. 这个后面我还会谈到.
现在有一个很大的问题是每支持一个新的平台(浏览器+操作系统的组合)都可能会遇到新的兼容性问题, 测试正可以解决这个问题. 毕竟没人能同时记住所有平台上的兼容性问题, 即使是富有多年工作经验的人也可能会被新的浏览器bug所困扰.
JavaScript测试框架已经出现了很多种, 因此不少人也会问: 用什么来测试. 上个月我做了一次调查: 你所使用的JavaScript测试框架, 大概收到了两千多个回复. 从图中可以看到: 前面几个选项数量不少, 但是更多的数量分散在长尾中, 甚至有些测试框架只有一个人在用, 或者是自己写成的测试框架. 然而, 不写测试的数量还要大. 完全不测试JavaScript的人数几乎占了一半. 恐怕实际上不写测试的比例还要更大.
JavaScript测试框架的组成跟传统的测试框架结构基本一致. 其中测试套件(Test Suite)包含多个测试(Test), 而每个测试又有一组断言(Assertion), 断言就是用来描述"这些为真, 那些为假"的方法. JavaScript测试需要能处理异步测试, 比如Ajax测试, 动画, 延时等, 测试框架要了解这些异步事件的结束. 然后还要有一个总的驱动(Test Runner), 把这些测试加载运行起来. 这样你只要用浏览器打开一个URL, 所有的测试都会自动跑完.
既然有些测试框架只有一个人在用, 这里我想说的是: 自己写一个测试框架很容易. 要想理解JavaScript测试框架的运作, 最好就是自己写一个测试框架, 而且真的很容易很容易. 比如基本的断言部分只要4-5行代码的样子. 这里我来举个例子.
我们先创建一个ul, 然后看这里的两个断言, 我们传入true会得到一个成功的测试并显示成绿色, 而传入假值[包括0, 空字符串, null等]则会失败, 显示为红色, 然后把测试结果记录在此. 我们为断言创建li元素, 用class表示通过/失败. 这就是测试框架最核心的部分, 每个测试框架都是这么干的: 记录测试结果, 然后用可视化的方法显示出来, 整个过程很简单.
接下来要做的是: 按照测试套件把测试结构化地组织起来. 一般来说一个测试中的断言都围绕着一个同方法. 只要多写这么几行, 就能把断言分组. 下面是测试的代码: 我们把一组断言放到一个函数中表明他们有相互关联. 这种语法很简明直接, 而且框架结构也不复杂, 只有十来代码.
最后我们要实现异步测试. 我来刷新一下这个页面看看: 这两个断言测试了setTimeout--当然现实中我们没必要测试它. 我们这里调用pause()函数就可以让测试中止运行, 后面的测试都将在队列中等待. 当我们再调用resume()的时候, 测试又将继续运行下去. 这样我们可以在同一个页面中混合运行同步与异步测试. 这样一个测试框架只需二十几行代码. 既然写一个测试框架都如此简单, 没理由不来上手用一下.
下面这个页面其实就是用来加载测试的, 看上去很华丽. [然后这一坨没怎么听清...大意就是讲需要一个测试驱动来加载与运行测试, 有些测试框架有自己的加载过程而有些没有, 而Dojo的测试驱动更华丽一点, 可以自行选择暂停/继续.]
我估计今晚就会有人开始动手写自己的测试框架, 不过现今流行的测试框架已经很优秀了. 根据我的调查, QUnit, JSUnit, Selenium和YUI Test最为流行. 后面就是长尾. 下面我会谈谈这前四名, 但愿能给你带来一点JavaScript测试的感觉.
我认为单元测试(Unit Testing)的目的就是把代码按照逻辑分割开来, 一般会按照方法[亦即函数]来分割. 每一组断言都在围绕API中的一个方法来测试. 这很适合测试库/框架的API. 适合单元测试的流行框架包括JSUnit, QUnit和YUI Test.
JSUnit给我的感觉很..2001, 感觉它是生于那个年代的JavaScript代码. 我这里没有展示它的源码. 不过如果你看一眼其中的驱动部分, 我用Firebug查找运行的测试数目的时候, DOM里全是, , 不过好像没什么
--他们直接用frame来布局, JSUnit很久没有维护了. JSUnit基本上就是Java中的JUnit移植到JavaScript的样子.
这就是JSUnit的测试文件, 代码风格很像Java. 对这个框架我很失望, 陈旧且缺乏更新. 有些人还在使用也许只是因为习惯而已.
下面是YUI Test, 与YUI2一起发布于去年年末, 新版与YUI3共同发布. 虽然问世至今仅一年但已经很是流行--因为它的确很优秀. 对异步测试也有支持. 我最喜欢它的事件模拟功能. 比如你想模拟对键盘输入, 鼠标点击等事件的测试, YUI对此有很好的支持.
这就是YUI Test2的测试代码, 结构很清晰, 支持setup/tearDown为每个测试做准备/清理工作. 测试驱动最终在网页中生成一个弹出式的控制台来包括结果. YUI Test3语法上更好一点, 但整体区别不大. 控制台更光鲜一点, 加上了一点色彩渐变, 并且加入了运行时间的统计.
然后是QUnit, 这是我们用来测试jQuery的框架. 我们把QUnit写成了独立的项目, 现在它不再依赖jQuery运行. 它支持异步测试, 依模块分类--比如把所有Ajax测试归结到一起, 还有测试时限, 比如限定一个测试必须在10秒钟内结束, 如果超时的话就按照失败处理, 这在异步测试中很有用.[然后又是一坨听不清的]
QUnit的语法跟我之前展示的迷你测试框架一样. 这里有测试, 断言, 模块分组. 然后这个页面是驱动部分. [屏幕切换中] 这个是QUnit的网站, 完整的API文档都在这, 功能很少, 毕竟测试只需要这么几个方法, 不必搞得很复杂.
下面我要展示一些实际的测试代码, 这是我们用来测试jQuery的代码. 我们为jQuery写了两千多个测试, 而且越写越多. 完善的测试覆盖给jQuery项目帮助很大, 确保我们做出修改时不会制造麻烦. 特别我们团队在地理上高度分散, 没法聚到一起解决问题, 因此这样的自动化测试显得格外重要. 还有一个功能是指定测试中运行的断言数目. 比如我们这里指定要运行16个, 如果实际上只完成了15个, 那么测试会被记作失败. 这样可以确保一些条件分支中的测试能运行[异步测试同样受益]. 也可以双击某一个测试来单独运行它.
我还写了一个测试框架名为FireUnit. 是Firebug的单元测试插件. 可以在Firebug里完成测试. 在FireUnit的网站上就嵌入了一段测试代码. 在Firebug控制台里可以看到链接至源码的测试结果, 而且包括调用栈跟踪.
社区正在为标准化做出努力. CommonJS规范致力于为客户端与服务器端定义一个通用的API. 包括模块, 打包, 单元测试等. 前段时间我跟他们交流了一下, 就API的设计达成了一致观点, QUnit也接受了这个API. 希望JavaScript测试框架能向这个方向靠拢.
下面我想讲一点行为测试与行为测试开发. 这方面有两个较为流行的框架是Screw.Unit与JSSpec. 行为测试更适合于测试实际的网站而不是简单的API测试. 虽然对API的测试不难, 但问题是我们大多数人在做的是网站而非框架/库, 而对实际网站的测试也不能忽视. 一个很优秀的工具是Selenium, 它能记录并自动测试网站上的行为. 他们还做了一个Selenium IDE, 是Firefox扩展, 用来记录操作并重放.
这里我来展示一下, 这是它的IDE窗口, 点击录制, 然后我回到网页这边, 点击下载, 搜索教程. 然后在IDE这边可以看到我们的点击和等待操作都被记录下来了. 然后我们停止记录并重放这些动作, 一切都能自动化地重现了. 操作记录文件可以保存下来, 并储存成Ruby, Java等多种语言文件. Selenium可以据此在后台自动启动浏览器, 运行测试, 并将测试结果与服务器端测试报告整合起来. 功能的确很强大.
Selenium还能侦测页面元素, 比如验证页面上是否出现了某些文字, 某个按钮是否存在等等. 这可以用来验证网站是否正常工作, 包括内容动态的页面, 比如验证JavaScript制作的动态UI.
JavaScript测试的又一个发展方向是在服务器端模拟浏览器环境. 一般用Java或JavaScript写成, 然后配合Rhino引擎测试. Rhino是JavaScript的一个Java实现. 因此它脱离浏览器单独运行.
这几个框架可以帮你在Rhino中模拟浏览器环境, 用以完成无浏览器的测试. 其中我写了一个Env.js, 完全用JavaScript模拟浏览器环境. 下面我再演示下.[无意义叨叨一阵]. 这里我给window.location赋值用来加载页面, 有window和document对象. Env.js的特色是: 当你检视一个函数时可以看到它的源代码而不是[native code], 因为它完全是用JavaScript写成的.
Env.js制造的浏览器环境可以运行起大多数流行的JavaScript框架, 比如jQuery, Prototype, 用它们做服务器端的程序. 我听说有人基于这个环境写了网页抓取的程序, 当时我就震惊了--毕竟Env.js还没有达到生产环境可用的程度. 甚至还有人据此写了完整的服务器端框架--我不想说这玩意很像Rails, 不过它的确很像. 但是它能在服务器端直接操作DOM, 改变内容, 然后再发送到客户端. 这样客户端与服务器端可以同时运行诸如表单验证, 模板等代码. 服务器端JS是一个很大的话题, 这里我不再多谈, 但是我建议各位关注一下.
关于模拟浏览器测试我想再提一点, 尽管运行速度很快, 反馈及时, 但是这还是不能替代真正的浏览器测试. 它距离现实情况太远, 这些模拟框架都是作者们在尝试模拟浏览器的工作方式. 包括一些模拟器--比如在Mac系统中模拟Win平台下的IE, 或者桌面上的手机浏览器模拟--这些都不能跟真实的测试相比拟.
现在问题又来了, 我们要测试多个平台, 但有需要能自动化测试的方法. Selenium有这么一套完整的解决方案, 这里有一个组件是Selenium Grid. 它可以把你的测试推送到Amazon云服务上, 在多个平台自动进行浏览器测试.
我写的一个方案叫TestSwarm. 工作方式与Selenium Grid相似. 不过区别在于你要自己假设服务器并推送测试, 而且需要有志愿者访问服务器来做测试. 稍候我将详述.
不过在你的公司里, 现实情况可能是有那么一两台测试机器, 安装了各种浏览器, 然后你想自动启动浏览器来运行测试. 这种情况我们只需要引导浏览器即可. 这里大多数引导工具是用Java写成的. 也可以把这些工具集成到开发流程中, 让他们自动报告测试结果. 比如这里Google发布的JsTestDriver, 它与Eclipse集成良好, 可以自动地即时获取测试反馈. 可行的解决方案还有很多.
下面我要谈的是扩展问题. 我们要支持的浏览器平台越多, 自动化测试越困难. 特别是对每次代码提交都要测试时难度更大. 因此JavaScript测试的扩展性并不好.
我写TestSwarm的原因就在这里. 开发者只需建立起中心服务器, 服务器从开发者处收集测试套件, 测试者访问服务器就可以开始自动运行测试, 测试的结果将被服务器自动统计起来. 这张图简单展示了它的工作机理.
下面要谈的是手动测试. 比如你想测试UI操作流程并记录整个操作过程. 这就要向测试者给出指示: 这有个对话框, 把它拖拽到另一边, 拖拽正常吗? 有否出现视觉上的瑕疵? 这可以是分布式手动测试的一个思路.
我们建立了高分榜来鼓励测试. 运行测试多的人可以拿高分, 我们可能会依照分数发点小礼品. 我再展示一下: 这里是TestSwarm的站点, 上面显示的是已经连接到服务器的不同浏览器数目. [叨叨, 不知所云] 下面是jQuery项目中与代码版本历史相结合的测试结果一览, 垂直方向上的是修订版, 水平方向是浏览器. 实际测试的浏览器种类远超过之前提到的11种. 现在差不多有50种了. 这里有一次版本提交造成了测试失败. Opera是测试失败最多的浏览器. 这种测试可以帮我们更好地了解全局, 比如Windows 2008上的IE8可能跟Vista上的IE8行为不同, 未来Win7上的IE8可能还会有变异.
下面我展示一下如何运行测试. 在这输入我的用户名, 然后点连接. 这里会收到jQuery UI测试套件并在后台自动运行. 然后客户端会周期性地询问服务器是否有新的测试任务. 所以我们只要打开浏览器, 开几个标签页, 就能自动运行测试并收集测试结果.
[各种念叨...大意是说这样大规模的分布式测试有助于抹去个体不确定性(比如个人网速慢, CPU忙碌等)造成的测试误差]
http://bbs.blueidea.com/viewthread.php?tid=2981536
it知识库:2009年11月John Resig(jQuery创始人)在荷兰Fronteers大会上的演讲.,转载需保留来源!
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。