本书是一本完整的、基于最佳实践的JavaScript敏捷测试指南,同时又有着测试驱动开发方法(TDD)所带来的质量保证。领先一步的JavaScript敏捷开发者Christian Johansen的讨论涵盖了将最先进的自动化测试用于JavaScript开发环境的方方面面,带领读者走查整个开发的生命周期,从项目启动到应用程序部署。本书的主要内容包括:掌握自动化测试和TDD;构建有效的自动化测试工作流;测试浏览器和服务器两端的代码(使用Node.js)、运用TDD构建更加干净的API、更加模块化的代码以及更健壮的软件;编写可测试的代码;使用测试桩和模拟对象,将各个单元单独进行测试;通过重构持续进改代码;走查全功能软件的创建和自动化测试。
封面图
目录
- 译者序
- 前言
- 致谢
- 关于作者
- 第一部分测试驱动开发
- 第1章 自动化测试1
- 1.1 单元测试1
- 1.1.1 单元测试框架2
- 1.1.2 JavaScript日期函数strftime2
- 1.2 断言5
- 1.3 测试函数、用例和套件7
- 1.4 集成测试10
- 1.5 单元测试的好处12
- 1.5.1 回归测试12
- 1.5.2 重构12
- 1.5.3 跨浏览器的测试13
- 1.5.4 其他的一些好处13
- 1.6 单元测试中的陷阱13
- 1.7 小结13
- 第2章 测试驱动开发的过程15
- 2.1 测试驱动开发的目的与目标15
- 2.1.1 开发顺序的颠倒15
- 2.1.2 测试驱动开发中的设计15
- 2.2 过程16
- 2.2.1 步骤1:编写一个测试17
- 2.2.2 步骤2:观看测试失败17
- 2.2.3 步骤3:使测试通过18
- 2.2.4 步骤4:重构以消除冗余20
- 2.2.5 打肥皂、冲洗、重复20
- 2.3 让测试驱动开发简便易行21
- 2.4 测试驱动开发的好处21
- 2.4.1 有效的代码21
- 2.4.2 遵循单一职责原则22
- 2.4.3 强制有意识的开发22
- 2.4.4 提高生产效率22
- 2.5 小结22
- 第3章 行业工具23
- 3.1 xUnit测试框架23
- 3.1.1 行为驱动开发23
- 3.1.2 持续集成23
- 3.1.3 异步测试24
- 3.1.4 xUnit测试框架的特点24
- 3.1.5 断言25
- 3.1.6 依赖关系25
- 3.2 浏览器内的测试框架26
- 3.2.1 YUI Test26
- 3.2.2 其他浏览器内的测试框架28
- 3.3 无头测试框架29
- 3.3.1 交叉检查29
- 3.3.2 Rhino与env.js29
- 3.3.3 无头测试框架的缺点30
- 3.4 一个掌管一切的测试运行器30
- 3.4.1 JsTestDriver是怎样工作的30
- 3.4.2 JsTestDriver的缺点31
- 3.4.3 安装31
- 3.4.4 在IDE里使用JsTestDriver35
- 3.4.5 提高命令行效率36
- 3.4.6 断言37
- 3.5 小结38
- 第4章 从测试中学习39
- 4.1 用单元测试探索JavaScript39
- 4.1.1 观察法编程的缺陷41
- 4.1.2 学习测试的最佳点41
- 4.2 性能测试42
- 4.2.1 基准和相对性能43
- 4.2.2 性能评测和定位瓶颈50
- 4.3 小结51
- 第二部分开发人员的JavaScript
- 第5章 函数53
- 5.1 定义函数53
- 5.1.1 函数声明53
- 5.1.2 函数表达式54
- 5.1.3 Function构造器55
- 5.2 调用函数56
- 5.2.1 arguments对象56
- 5.2.2 形参和arguments57
- 5.3 作用域和执行上下文58
- 5.3.1 执行上下文59
- 5.3.2 变量对象59
- 5.3.3 活动对象60
- 5.3.4 全局对象60
- 5.3.5 作用域链61
- 5.3.6 再访函数表达式62
- 5.4 this关键字64
- 5.4.1 隐式地设置this65
- 5.4.2 显式地设置this65
- 5.4.3 使用原初类型当做this66
- 5.5 小结67
- 第6章 函数和闭包的应用68
- 6.1 绑定函数68
- 6.1.1 this被丢弃:一个Lightbox的例子68
- 6.1.2 通过一个匿名函数解决this问题69
- 6.1.3 Function.prototype.bind70
- 6.1.4 绑定参数71
- 6.1.5 局部套用73
- 6.2 被立即调用的匿名函数74
- 6.2.1 Ad Hoc作用域74
- 6.2.2 命名空间76
- 6.3 状态函数79
- 6.3.1 生成唯一的标识符79
- 6.3.2 迭代器81
- 6.4 记忆84
- 6.5 小结86
- 第7章 对象和原型继承87
- 7.1 对象和属性87
- 7.1.1 属性访问88
- 7.1.2 原型链89
- 7.1.3 通过原型链实现对象扩展90
- 7.1.4 可枚举属性91
- 7.1.5 属性的特性94
- 7.2 使用构造器创建对象98
- 7.2.1 prototype和[[Prototype]]98
- 7.2.2 使用new创建对象98
- 7.2.3 构造器原型99
- 7.2.4 构造器的问题102
- 7.3 伪经典结构的继承103
- 7.3.1 继承函数104
- 7.3.2 访问[[Prototype]]105
- 7.3.3 实现super105
- 7.4 封装和信息隐藏110
- 7.4.1 私有方法110
- 7.4.2 私有成员和特权方法112
- 7.4.3 函数式继承113
- 7.5 对象组合和混搭体115
- 7.5.1 Object.create方法115
- 7.5.2 tddjs.extend方法117
- 7.5.3 混搭体120
- 7.6 小结121
- 第8章 ECMAScript 5122
- 8.1 JavaScript不远的未来122
- 8.2 对象模型的更新123
- 8.2.1 属性的特性123
- 8.2.2 原型继承125
- 8.2.3 Getter和Setter127
- 8.2.4 使用属性的特性128
- 8.2.5 作为属性标识符的保留关键字131
- 8.3 严格模式131
- 8.3.1 启用严格模式131
- 8.3.2 严格模式带来的改变132
- 8.4 各种新内容及改进134
- 8.4.1 原生的JSON134
- 8.4.2 Function.prototype.bind135
- 8.4.3 关于数组135
- 8.5 小结135
- 第9章 不唐突的JavaScript137
- 9.1 不唐突的JavaScript的目标137
- 9.2 不唐突的JavaScript的规则138
- 9.2.1 唐突的标签式面板138
- 9.2.2 简洁的标签式面板标记140
- 9.2.3 TDD和渐进增强141
- 9.3 不要做任何假设141
- 9.3.1 不要假设只有你一个人141
- 9.3.2 不要假设标记是正确的142
- 9.3.3 不要假设所有用户的需求都一样142
- 9.3.4 不要假设支持142
- 9.4 规则何时适用143
- 9.5 不唐突的标签式面板的例子143
- 9.5.1 建立测试143
- 9.5.2 tabController对象145
- 9.5.3 activateTab方法147
- 9.5.4 使用标签控制器149
- 9.6 小结152
- 第10章 特性检测154
- 10.1 浏览器嗅探154
- 10.1.1 用户代理嗅探154
- 10.1.2 对象检测155
- 10.1.3 浏览器嗅探的状态156
- 10.2 正确使用对象检测156
- 10.2.1 检查对象是否存在156
- 10.2.2 类型检查157
- 10.2.3 原生和宿主对象157
- 10.2.4 使用测试样例160
- 10.2.5 何时测试161
- 10.3 特性检测DOM事件161
- 10.4 特性检测CSS属性163
- 10.5 跨浏览器事件处理例程164
- 10.6 使用特性检测167
- 10.6.1 向前进167
- 10.6.2 无法检测到的特性167
- 10.7 小结168
- 第三部分JavaScript测试
- 驱动开发实践
- 第11章 观察者模式169
- 11.1 JavaScript中的观察者169
- 11.1.1 观察物库170
- 11.1.2 设置环境170
- 11.2 添加观察者171
- 11.2.1 第一个测试171
- 11.2.2 重构173
- 11.3 检查观察者174
- 11.3.1 测试175
- 11.3.2 重构177
- 11.4 通知观察者177
- 11.4.1 确保观察者被调用177
- 11.4.2 传递参数178
- 11.5 错误处理179
- 11.5.1 添加假的观察者179
- 11.5.2 有问题的观察者180
- 11.5.3 记录调用顺序181
- 11.6 观察任意对象182
- 11.6.1 让构造器成为过去式183
- 11.6.2 用对象来替换构造器185
- 11.6.3 重命名方法186
- 11.7 观察任意事件187
- 11.7.1 支持observe中的事件187
- 11.7.2 支持notify中的事件188
- 11.8 小结191
- 第12章 抽象化浏览器区别:Ajax192
- 12.1 以测试驱动来开发一个请求API192
- 12.1.1 发现浏览器的不一致192
- 12.1.2 开发策略192
- 12.1.3 目标193
- 12.2 实现请求接口193
- 12.2.1 项目布局193
- 12.2.2 选择接口风格194
- 12.3 创建一个XMLHttpRequest对象194
- 12.3.1 第一个测试194
- 12.3.2 XMLHttpRequest背景知识195
- 12.3.3 实现tddjs.ajax.create196
- 12.3.4 更强的特性检测197
- 12.4 编写Get请求198
- 12.4.1 需要一个URL198
- 12.4.2 为XMLHttpRequest对象
- 创建桩199
- 12.4.3 处理状态的变化205
- 12.4.4 处理状态变化206
- 12.5 使用Ajax API209
- 12.5.1 集成测试210
- 12.5.2 测试结果211
- 12.5.3 微妙的麻烦212
- 12.5.4 本地请求213
- 12.5.5 测试状态214
- 12.6 发出POST请求217
- 12.6.1 为POST做准备217
- 12.6.2 发送数据220
- 12.6.3 设置请求头信息225
- 12.7 重审请求API225
- 12.8 小结229
- 第13章 使用Ajax和Comet将数据流式化230
- 13.1 数据轮询230
- 13.1.1 项目结构230
- 13.1.2 轮询器:tddjs.ajax.poller231
- 13.1.3 定时器测试238
- 13.1.4 可配置的头和回调242
- 13.1.5 单行接口245
- 13.2 Comet247
- 13.2.1 永不消失的框架247
- 13.2.2 流式化XMLHttpRequest248
- 13.2.3 HTML5248
- 13.3 长轮询的XMLHttpRequest248
- 13.3.1 实现长轮询支持248
- 13.3.2 避免缓存问题251
- 13.3.3 特性检测252
- 13.4 Comet客户端252
- 13.4.1 消息格式253
- 13.4.2 介绍ajax.cometClient254
- 13.4.3 分发数据255
- 13.4.4 添加观察者258
- 13.4.5 服务器连接259
- 13.4.6 跟踪请求和接收到的数据264
- 13.4.7 发布数据267
- 13.4.8 特性检测267
- 13.5 小结267
- 第14章 基于Node.js的服务器端
- JavaScript269
- 14.1 Node.js运行时环境269
- 14.1.1 环境设置269
- 14.1.2 起点271
- 14.2 控制器272
- 14.2.1 CommonJS规则模块272
- 14.2.2 定义模块:首测272
- 14.2.3 建立控制器273
- 14.2.4 基于POST方式添加消息274
- 14.2.5 对请求做出响应280
- 14.2.6 将应用程序用于实践281
- 14.3 域模型和存储器283
- 14.3.1 创建聊天室283
- 14.3.2 Node中的输入输出283
- 14.3.3 添加聊天信息283
- 14.3.4 获取聊天信息287
- 14.4 Promises290
- 14.4.1 用promise重构addMessage方法291
- 14.4.2 使用promise对象294
- 14.5 事件产生器295
- 14.5.1 让chatRoom对象成为一个事件
- 产生器295
- 14.5.2 等待聊天信息297
- 14.6 回到控制器300
- 14.6.1 完成Post方法300
- 14.6.2 通过GET方法输出数据流302
- 14.6.3 响应头和响应体307
- 14.7 小结308
- 第15章 TDD与DOM操作:聊天室客户
- 端程序309
- 15.1 客户端的蓝图309
- 15.1.1 目录结构309
- 15.1.2 选择方法310
- 15.2 用户表单311
- 15.2.1 视图的设置311
- 15.2.2 处理提交事件316
- 15.2.3 特征测试324
- 15.3 用Node.js后端使用客户端325
- 15.4 聊天信息列表327
- 15.4.1 设置模型327
- 15.4.2 设置视图330
- 15.4.3 添加聊天信息331
- 15.4.4 来自同一用户的重复内容334
- 15.4.5 特征测试335
- 15.4.6 动手实验335
- 15.5 聊天信息表单336
- 15.5.1 建立测试用例337
- 15.5.2 设置视图337
- 15.5.3 发布聊天信息339
- 15.5.4 特征测试342
- 15.6 聊天室客户端的最终版本343
- 15.6.1 收尾工作344
- 15.6.2 部署时的注意事项346
- 15.7 小结347
- 第四部分测试模式
- 第16章 模拟对象和创建桩349
- 16.1 测试替身概述349
- 16.1.1 特技替身349
- 16.1.2 假对象350
- 16.1.3 哑对象350
- 16.2 测试验证351
- 16.2.1 状态验证351
- 16.2.2 行为验证351
- 16.2.3 验证策略的作用352
- 16.3 桩352
- 16.3.1 使用桩来避免不方便的接口352
- 16.3.2 使用桩让系统通过特定代码路径353
- 16.3.3 使用桩来制造麻烦353
- 16.4 测试间谍354
- 16.4.1 测试间接输入354
- 16.4.2 检测调用细节355
- 16.5 使用桩库355
- 16.5.1 创建桩函数355
- 16.5.2 给方法创建桩356
- 16.5.3 内建行为验证358
- 16.5.4 创建桩和Node.js359
- 16.6 模拟对象360
- 16.6.1 恢复被模拟的函数360
- 16.6.2 匿名的模拟对象361
- 16.6.3 多重期望361
- 16.6.4 对this值的期望362
- 16.7 模拟对象还是桩363
- 16.8 小结364
- 第17章 编写良好的单元测试365
- 17.1 提高可读性365
- 17.1.1 清楚地命名测试以表明意图365
- 17.1.2 用设置、应用和验证块对测试
- 进行结构化367
- 17.1.3 使用更高级的抽象保持测试的
- 简单性367
- 17.1.4 减少重复,而不是减少明确性369
- 17.2 将测试作为行为规范370
- 17.2.1 一次只测试一种行为370
- 17.2.2 每个行为只测试一次371
- 17.2.3 在测试中隔离行为371
- 17.3 与测试中的缺陷做斗争373
- 17.3.1 在测试通过之前运行它们373
- 17.3.2 首先编写测试374
- 17.3.3 搅乱和破坏代码374
- 17.3.4 使用JsLint374
- 17.4 小结375
- 参考文献376