JavaScript 组件之旅(四):测试 JavaScript 组件


Posted in Javascript onOctober 28, 2009

JavaScript 组件之旅(四):测试 JavaScript 组件
本期,我们要讨论的话题是 JavaScript 的测试,以检查组件的状态和工作方式是否符合预期,还会介绍一个可以方便编写测试用例的测试方法。这里说的测试当然是使用自动化的测试手段,这是软件质量保证(QA)的重要环节。就本系列文章介绍的 Smart Queue 来说,我们的测试目标包括:

  • Task 对象的创建第二期的代码提供了多种创建方式,需要测试对象创建后的状态。
  • Queue 内的任务运行次序:我们提供了两种改变运行次序的方式:优先级和依赖配置,同样也要测试各种配置对次序的影响。

对于第一个目标,只需检查对象创建后的属性是否符合预期即可。我们已经多次提到“符合预期”,断言(Assert)正是为此而设计的。简单的说,断言就是确保所测试的表达式结果为“真”,否则,以某种方式通知测试人员,并帮助其定位断言失败的测试用例。
第二个目标稍稍有点复杂。由于我们在组件编码实现的时候,将排序后的队列(_sorted)隐藏在了闭包中,所以外部是无法访问的。有两种方法可以考虑:(1)重构代码,增加代码的可测试性,又有两种重构方法:(a)设置 debug 开关,打开时将 _sorted 暴露给外部;(b)增加独立文件,以构建的方式拼接代码最终生成一个测试版本。(2)测试行为的结果而不是过程,前一种方法实质上是深入到组件的运行时状态,而这个方法只是检查组件的运行结果。本期选用后一种种测试方式,第一种测试方式留给有兴趣的读者练习:)

需要说明的是,我个人不赞成第一种的方法a. 为什么呢?我先说一下这个任务队列的设计理念:

  • 它只是一个队列,只负责“按需”调整任务的运行次序,不关注任务的个体细节。换句话说,它操作整体的任务,而不关心任务具体的行为和表现。
  • 它是个安全的队列,使用者(第一期提到的“客户”)可以放心把任务添加进去,不用担心这个任务信息会被其他客户看到。需要说明的是,第二期实现代码中有 SmartQueue.Queue = [[], [], []], 结果是外部可以访问到队列项。代码仅供介绍之用,你可以安全地删除 SmartQueue.Queue = 来达成安全控制。

回到刚才讨论的话题,设置 debug 开关后,任务信息就潜在的泄漏可能性。进一步地,继续改造代码也可以达成在使用 debug 开关时的安全性,做法是将开关的控制放在 SmartQueue 的构造函数中,这样要求 SmartQueue 实现 Singleton 模式(见上一篇文章);一旦创建对象后,不允许修改闭包内的 debug 标记。

在编写具体测试代码前,我们设计了一个测试方法,以简化测试代码(主要是用例)的编写。简单地说,就是将测试用例与测试本身的代码分离——前者以语义良好的方式编写,后者是一次性编写,用于处理前者设定的测试用例。用例编写者需要写格式形如这样的代码:

<ul id="J_test_cases">
    <li>
      <pre>task = new sq.Task({fn: function() { log('unamed') }})</pre>
      <ul>
        <li>typeof task.fn === 'function'</li>
        <li>task.name === 't0'</li>
        <li>task.level === 1</li>
        <li>task.dependencies.length === 0</li>
        <li>task.context == window</li>
      </ul>
    </li>
    <li>
      <pre>task = new sq.Task({fn: function() { log('unamed') }, name: 'hello'})</pre>
      <ul>
        <li>task.name === 'hello'</li>
        <li>task.level === 1</li>
      </ul>
    </li>
  </ul>

ul li pre (CSS 选择器路径,下同)中写要测试的代码,相当于前置操作;ul ul li 中对这个代码进行断言测试,可以编写多条断言。这里建议对基本数据类型使用 ===!=== 运算符以加强对数据类型的预期判断。

接下来,我们编写两个 helper 方法用来输出和测试:

function log(str) {
  node.value += str + '\n';
}

function assert(expression) {
  var flag;
  eval('flag = ' + expression);
  return typeof(flag) === 'boolean' && flag;
}

log 用来向文本框追加信息,assert 用来测试传入表达式的值。测试方法如下(这里使用了 jQuery):

var sq = SmartQueue, task, total = 0, passed = 0, failed = 0;
$('#J_test_cases').children().each(function(index) {
  eval($('pre', this).text());
  task.register();
  $('li', this).each(function() {
    var item = $(this);
    var flag = assert(item.text());
    if(flag) passed ++; else failed ++;
    item.prepend((flag ? '<font color="green">[PASS]</font>' : '<font color="red">[FAIL]</font>') + ' ');
    total++;
  }).wrap('<pre></pre>');
}).end().before('<p>Total: ' + total + ', passed: '+ passed +', failed: ' + failed + '</p>');
sq.fire();

这个结构还可改进一下,比如输出测试说明而不是具体的代码,也可以增加后置操作,这里就不再演示了。你还可以查看完整的测试页面,含有 23 个测试用例和完整的测试实现。

~~~~~~~~~~~~~ 八卦分割线 ~~~~~~~~~~~~~

好吧,我们已经体会到了思考和行动的乐趣,走到了系列文章的尾声,但这只是开始。我们经历了一个很小的实用组件的实现全过程,领略到了 JavaScript 世界的精彩,让我们继续前行~

Javascript 相关文章推荐
ie 调试javascript的工具
Apr 29 Javascript
javascript 正则替换 replace(regExp, function)用法
May 22 Javascript
基于Jquery 解决Ajax请求的页面 浏览器后退前进功能,页面刷新功能实效问题
Dec 11 Javascript
jQuery实现长文字部分显示代码
May 13 Javascript
cookie 最近浏览记录(中文escape转码)具体实现
Jun 08 Javascript
javascript 手动给表增加数据的小例子
Jul 10 Javascript
js仿支付宝填写支付密码效果实现多方框输入密码
Mar 09 Javascript
把JavaScript代码改成ES6语法不完全指南(分享)
Sep 10 Javascript
vue用递归组件写树形控件的实例代码
Jul 19 Javascript
JS 遍历 json 和 JQuery 遍历json操作完整示例
Nov 11 jQuery
使用js实现单链解决前端队列问题的方法
Feb 03 Javascript
vue修改Element的el-table样式的4种方法
Sep 17 Javascript
JavaScript 组件之旅(三):用 Ant 构建组件
Oct 28 #Javascript
JavaScript 组件之旅(二)编码实现和算法
Oct 28 #Javascript
JavaScript 组件之旅(一)分析和设计
Oct 28 #Javascript
js 数组实现一个类似ruby的迭代器
Oct 27 #Javascript
jquery 操作单选框,复选框,下拉列表实现代码
Oct 27 #Javascript
javascript获得CheckBoxList选中的数量
Oct 27 #Javascript
基于jQuery的日期选择控件
You might like
PHP中“简单工厂模式”实例代码讲解
2012/09/04 PHP
memcache命令启动参数中文解释
2014/01/13 PHP
php中使用getimagesize获取图片、flash等文件的尺寸信息实例
2014/04/29 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
PHP中类的继承和用法实例分析
2016/05/24 PHP
php实现base64图片上传方式实例代码
2017/02/22 PHP
PHP并发查询MySQL的实例代码
2017/08/09 PHP
解决 FireFox 下[使用event很麻烦] 的问题.
2006/08/22 Javascript
js实现iframe动态调整高度的代码
2008/01/06 Javascript
提示$ is not defined错误分析及解决
2013/04/09 Javascript
javascript实现焦点滚动图效果 具体方法
2013/06/24 Javascript
Document.body.scrollTop的值总为零的快速解决办法
2016/06/09 Javascript
原生JS版和jquery版实现checkbox的全选/全不选/点选/行内点选(Mr.Think)
2016/10/29 Javascript
vue中如何引入jQuery和Bootstrap
2017/04/10 jQuery
js 用于检测类数组对象的函数方法
2017/05/02 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
微信小程序使用map组件实现路线规划功能示例
2019/01/22 Javascript
node.js处理前端提交的GET请求
2019/08/30 Javascript
解决layui表格的表头不滚动的问题
2019/09/04 Javascript
koa2 数据api中间件设计模型的实现方法
2020/07/13 Javascript
基于Cesium绘制抛物弧线
2020/11/18 Javascript
python实现关键词提取的示例讲解
2018/04/28 Python
Python图像滤波处理操作示例【基于ImageFilter类】
2019/01/03 Python
python 表格打印代码实例解析
2019/10/12 Python
pandas中遍历dataframe的每一个元素的实现
2019/10/23 Python
tf.concat中axis的含义与使用详解
2020/02/07 Python
jupyter 中文乱码设置编码格式 避免控制台输出的解决
2020/04/20 Python
CSS3 icon font完全指南(CSS3 font 会取代icon图标)
2013/01/06 HTML / CSS
波兰运动鞋网上商店:e-Sporting
2018/02/16 全球购物
楼面经理岗位职责范本
2014/02/18 职场文书
2014年转正工作总结
2014/11/08 职场文书
保险公司客户经理岗位职责
2015/04/09 职场文书
幼儿园教师个人工作总结2015
2015/05/12 职场文书
萤火虫之墓观后感
2015/06/05 职场文书
中小学生安全教育观后感
2015/06/17 职场文书
创业计划书之青年旅馆
2019/09/23 职场文书