本书是为软件测试领域相关人员编写的。前3章重在培养读者的软件测试意识。第4章是一个特殊的测试流程介绍。第5章详细阐述了类测试思想。第6~9章偏重于测试技术应用,着重介绍一款Xunit工具,并且加入了实际例子进一步告诉读者如何更好地应用。第10章介绍了测试后BUG,的处理手段和相关处理流程。在附录中还有作者的感悟和与测试相关的书籍的评价。
本书的特点在于“学以致用”,从测试的实际应用出发,结合大量的测试例子,使读者在测试层面上有更大的提高。
本书适合于实现人员、设计人员、测试人员以及测试管理者参考使用。
第1章 引言
1.1 读者对象
1.2 编码语言
1.3 软件测试方法快速浏览
1.4 联机文档
1.5 本书包含章节
1.6 源代码
1.7 样式
1.8 错误
1.9 作者联系方法
1.10 建议与评论
1.11 致谢
第2章 测试进阶
2.1 什么是软件测试
2.2 为什么需要软件测试
2.3 软件测试目的
2.4 软件测试原则
2.4.1 尽早和不断地测试
2.4.2 彻底的测试不可能
2.4.3 由小到大的测试范围
2.4.4 避免检查自己的代码
2.4.5 追溯至用户需求
2.4.6 考虑到各种输入
2.4.7 错误集中发生现象
2.4.8 跟踪测试错误结果
2.4.9 合理安排测试计划
2.4.10 错误的关联和依赖
2.4.11 测试结果的全面检查
2.4.12 及时更新测试
2.5 软件的可测试性
2.6 测试人员的素质
2.7 如何成为测试专家
2.8 测试人员最好拥有编程或者项目经验
2.9 本章总结
第3章 认识和理解
3.1 什么是软件BUG
3.2 测试团队的构成
3.3 测试认识的误区
3.3.1 测试工具和有效测试
3.3.2 测试天生是矮子
3.3.3 随意的测试
3.3.4 测试是一种想象
3.3.5 非专业人员做测试
3.3.6 测试是简单的事情
3.3.7 测试能保证软件质量
3.4 单元测试概论
3.4.1 单元测试提高开发速度和质量
3.4.2 单元测试是一种设计
3.4.3 单元测试的重要性
3.4.4 哪些代码呼唤单元测试
3.4.5 初试单元测试
3.5 单元测试的误区
3.5.1 单元测试不是规范
3.5.2 它浪费了太多的时间
3.5.3 它仅仅是证明这些代码做了什么
3.5.4 是不是可以不进行单元测试
3.5.5 集成测试将会抓住所有的Bug
3.5.6 成本效率不高
3.6 持续集成和测试
3.6.1 持续集成的优点
3.6.2 集成越频繁效果越好
3.6.3 使用配置管理工具
3.6.4 自动化创建过程
3.6.5 自测试的代码
3.7 测试用例
3.7.1 测试用例构成
3.7.2 测试用例范例
3.8 软件复审
3.8.1 软件复审内容
3.8.2 软件评审误区
3.8.3 流程图
3.8.4 复审参与角色
3.8.5 流程规约
3.9 测试自动化
3.9.1 使用测试工具的前提
3.9.2 测试自动化的绩效
3.10 自动化测试的存活周期
3.10.1 中介代码的变化
3.10.2 被测试代码的变化
3.10.3 更深入的测试探讨和研究
3.11 商业测试工具实现方式
3.11.1 对象识别模式
3.11.2 动作识别模式
3.11.3 两种模式的缺陷
3.12 本章总结
第4章 敏捷测试
4.1 软件的开发模式
4.1.1 瀑布模型
4.1.2 迭代模型
4.1.3 螺旋迭代模型
4.2 敏捷测试的存在理由
4.2.1 敏捷测试的价值观
4.2.2 敏捷测试的原则
4.2.3 敏捷测试的实践
4.2.4 敏捷测试是(不是)什么
4.2.5 测试何时是敏捷的
4.2.6 敏捷测试何时是有(没有)意义的
4.2.7 想成为一个敏捷测试者吗
4.2.8 敏捷测试团队的构成
4.3 敏捷测试涉及角色
4.3.1 测试经理
4.3.2 测试分析员
4.3.3 测试设计员
4.3.4 测试员
4.3.5 测试工具开发员
4.4 敏捷测试成熟度
4.4.1 初始阶段(I级)
4.4.2 已定义阶段(II级)
4.4.3 可持续集成阶段(III级)
4.4.4 可管理阶段(IV级)
4.4.5 持续优化阶段(V级)
4.5 敏捷测试过程
4.6 本章总结
第5章 类测试
5.1 类测试概念
5.1.1 类在UML中的描述
5.1.2 类测试的组成
5.1.3 类测试和传统单元测试
5.1.4 类的测试价值
5.1.5 类测试关联人员
5.1.6 类测试用例
5.1.7 类测试的阶段
5.1.8 类测试过程
5.1.9 类测试程度
5.2 确定类测试用例
5.2.1 根据前置和后置状态确定测试用例
5.2.2 根据状态转换确定测试用例
5.2.3 根据代码确定测试用例
5.3 构造类测试驱动
5.3.1 测试驱动器的构建前提
5.3.2 TestCase类设计
5.3.3 根据用例方法命名测试用例
5.3.4 根据前置条件和后置状态命名测试用例
5.3.5 类测试代码实例
5.3.6 测试结果
5.3.7 其他测试方式
5.4 测试构建的延伸
5.4.1 接口类的测试
5.4.2 抽象类的测试
5.4.3 抽象类测试改进
5.4.4 内类的测试
5.4.5 重载和覆盖测试
5.4.6 异常测试
5.5 本章总结
第6章 深入浅出Junit
6.1 单元级测试概述
6.1.1 Junit单元级测试的好处
6.1.2 System.out.println是不够的
6.1.3 成本的回收和再循环
6.2 哪里可以找到Junit
6.3 Junit的安装和配置
6.4 自动化测试框架
6.5 Test接口
6.6 Assert静态类
6.7 TestCase抽象类
6.8 TestResult结果类
6.9 TestSuite测试包类
6.10 TestListener监听者
6.11 Protectable保护接口
6.12 TestFailure失败类
6.13 本章总结
第7章 Junit GUI和扩展
7.1 概述
7.2 Junit的运行包
7.2.1 BaseTestRunner基运行类
7.3 Junit.textui字符执行包
7.3.1 TestRunner字符执行类
7.4 Junit.Awtui图形执行包
7.4.1 TestRunner图形执行类
7.5 Junit.Swingui图形执行包
7.5.1 TestRunner图形执行类
7.6 Junit扩展包
7.6.1 测试结果报告
7.6.2 多线程测试处理
7.6.3 增强异常测试
7.6.4 重复测试执行
7.6.5 额外的测试修饰
7.6.6 测试总环境初始化
7.7 Junit快速应用
7.7.1 如何运用Junit写一个简单测试程序
7.7.2 如何使用Junit的断言方法
7.7.3 如何使用Junit的suite方法
7.7.4 如何使用Junit的main方法
7.7.5 如何执行Junit测试
7.7.6 如何使用Junit初始化全局变量和实例
7.7.7 SimpleTestCase执行后效果
7.8 本章总结
第8章 Junit和类测试
8.1 Junit测试设计原则
8.1.1 不要测试简单的事
8.1.2 测试任何可能出现错误的地方
8.1.3 测试边界条件
8.1.4 作为详细设计文档和类文档的衍生
8.1.5 自动化
8.1.6 必须100%通过
8.1.7 测试重用
8.1.8 测试用例应该独立
8.1.9 测试依赖于接口
8.1.10 固定类方法的调用顺序
8.1.11 测试依赖于接口
8.2 类测试过程
8.2.1 构建单元级测试
8.2.2 CRC Cards
8.2.3 测试驱动复用
8.2.4 编码重构
8.2.5 持续集成
8.3 Product CRC Cards
8.3.1 Iproduct CRC Cards
8.3.2 Product CRC Cards
8.3.3 StoreHouse CRC Cards
8.4 Product代码
8.4.1 IProduct
8.4.2 Product类
8.4.3 StoreHouse类
8.5 Product测试
8.5.1 测试计划
8.5.2 测试数据
8.5.3 测试驱动代码
8.5.4 辅助说明
8.6 本章总结
第9章 Junit测试的延伸
9.1 录制功能点测试
9.2 抽象类测试(一)
9.3 抽象类测试(二)
9.4 私有方法测试
9.5 映射对象Mock
9.5.1 单元级测试和Mock
9.5.2 为什么使用Mock
9.5.3 高级测试行为
9.5.4 发现接口
9.5.5 Mock Objects的局限性
9.6 重构和MockObject
9.6.1 Bank类重构前
9.6.2 Bank类重构后
9.6.3 Mock Objects要点
9.6.4 Mock Objects简单范例
9.7 异常处理
9.8 随机测试
9.9 间隔测试
9.10 本章总结
第10章 软件BUG和管理
10.1 软件BUG和癌细胞
10.2 BUG的影响
10.3 BUG的产生
10.4 BUG如何穿透测试
10.5 BUG的种类
10.5.1 需求阶段的BUG
10.5.2 分析.设计阶段的BUG
10.5.3 实现阶段的BUG
10.5.4 配置阶段的BUG
10.5.5 短视将来的BUG
10.5.6 静态文档的BUG
10.6 Bug的具体分类
10.6.1 内存泄漏
10.6.2 程序运行时错误
10.6.3 程序语法的错误
10.6.4 未使用(死)代码的错误(一类)
10.6.5 编码标准的错误(二类)
10.6.6 命名惯例的错误(三类)
10.6.7 条件错误(一类)
10.6.8 循环错误(二类)
10.6.9 选择错误(三类)
10.6.10 多线程错误
10.6.11 读取和存储错误
10.6.12 集成错误
10.6.13 数据类型转换错误
10.6.14 版本错误
10.6.15 重用错误
10.6.16 Boolean错误
10.7 BUG的生命周期
10.7.1 BUG的流转状态关键字
10.7.2 BUG的严重等级
10.7.3 BUG的解决关键字
10.7.4 BUG处理的优先等级
10.8 BUG管理流程
10.8.1 如何提交系统中的BUG
10.8.2 使用自动BUG报告工具
10.8.3 通过电子邮件发送BUG报告
10.8.4 BUG详细内容信息
10.8.5 轻微的BUG报告
10.8.6 不知道归属的BUG
10.8.7 关闭BUG报告
10.8.8 接续的讨论信息
10.8.9 列出的具有特殊意义的BUG
10.8.10 重开.重分配的BUG
10.8.11 BUG的标题(特殊)
10.9 如何有效地报告BUG
10.9.1 千万别提“程序不好用”
10.9.2 不要盲目地报告BUG
10.9.3 让更多的事实说话
10.9.4 别把实现人员当傻瓜
10.9.5 冷静和细心
10.9.6 保持现场环境
10.9.7 真实的和建议的
10.9.8 提交问题的根源
10.9.9 最后的建议
10.10 本章总结
附录A 失败的项目
附录B 小集市里的谈话
B.1 序言
B.2 从测试文章看测试与开发
B.3 有关测试人生
B.4 关于测试到什么程度, 才能收手的问题
B.5 国外测试与国内测试的区别
B.6 如何做测试评估
B.7 作QA的就比作coding的差吗
B.8 大家一起来讨论测试的文档
B.9 数据库的测试
B.10 BUG如何分类
B.11 在什么时候项目提交给测试人员, 什么是最合理的
B.12 测试要有编程背景吗
B.13 编写测试用例
B.14 验收测试怎么做
B.15 如何创建测试模型
B.16 请问作为一个测试部的负责人, 应该如何组织和开展测试工作呢
B.17 集成测试需要专门的管理软件吗
B.18 来讨论一下测试的生命周期
B.19 经典BUG收集
B.20 大家觉得怎么做才能改变测试人员的地位
B.21 各种测试工具的主要功能和用途
B.22 测试负责人应该做些什么工作
附录C Web性能测试工具“StressTest”
附录D 推荐的相关书籍
D.1 序言
D.2 测试类书籍
D.3 需求分析类书籍
D.4 设计类书籍
D.5 代码实现和优化类书籍
D.6 软件工程类书籍
“可悲,那是因为我要面对太多错误的事。冰冷,那是因为我无法顾及别人的情绪。孤独,那是因为我住在一个边界之地。身后是无法穿透的黑暗,我守望着或者不停撕裂着错误的隔膜,阻挡无知的人继续迈向界后的深渊。
——创世纪寓言”
最近和公司同事nickle做了一次彻夜长谈,他在公司专职软件设计,使用UML语言绘制出软件模型然后转交给程序员进行编码。当我听他说曾经专职做过一年软件测试时感到非常吃惊,下意识地追问他为什么放弃测试工作改行为软件设计了,nickle是这样回答我的。
“软件测试这个行业在国内太新了,可以说什么都没有。找些相关的资料都比较困难,挂上Google搜索的全是英文的,如果找到了中文的你会发现它已经失去了实际应用价值”。他继续说:“比如你可以制定很多的规范来强制别人对你的输出(产生很多的标准输出工件),可你有细致的。能控制的测试后续流程吗?假设你有细致的。能控制的测试后续流程,可你的管理层能给你充分的资源来支持你的工作吗?假设你的管理层全心全意地支持你开展工作,可是相关人员能否正确理解你的意思,做好配合呢?就算上面的条件你都具备了,可你是否有勇气对你测试后的项目质量负全责?”
想到自己,也曾经在很深的夜里睡不着,是因为无法理解软件测试的真谛而在空旷的街道上满无目地行走。也曾经和一些同事激烈争吵过,是因为自己制订的一些标准输出不被别人重视。也曾经为自己的工作胆战心惊过,是因为自己负责测试的项目出现了严重的质量纰漏,更不用说忍受过多少次责骂和贬低了。可是在这一行中,还有多少人因为前途迷茫和无助而退出呢?正为“世间本来没有路,走的人多了就成了路”,所以我鼓励所有的现在正在从事软件测试工作的人不要因为别人的偏见。指责和不理解而放弃这项工作。
我跟别的同行交流听到过这样的话“你连编码都不会只能去做软件测试了”。“我为什么要请这么多的测试人员,一个就可以了”。“软件测试不就是把软件用一遍这么简单吗,有什么困难的?”。“我刚毕业,没有什么实际经验所以就做测试了”。“公司如果要裁员,先把测试部的人裁掉吧”等。我的同行james对我说:“软件测试已经成为了软件工程中最低级。最同单。最不需要知识积累的代名词。”在很多场合,软件开发者和项目管理者都将测试工作看成一件可有可无的事情。下面是我和一个程序员在工作上发生的冲突:
你是不是也碰到过这样的事情?我认为这是一个意识问题,我们需要改变人们对测试工作的态度,需要改变人们对测试工作的看法。能够让人们从开始就尊重软件测试工作,理解软件测试是在帮助他们,确保他们开发出来的产品是正确的。安全可靠的,这才是当前软件测试最需要解决的问题。
人总是狂热地追求把非理性的。不可控制的现实行为用逻辑的方法体现出来,在这两种状态转换的时候就会出现很多可预见和不可预见的问题。从这里你就大概可以知道问题出现的根源了,你甚至可以这样说“因为人的存在,这个世界才出现了很多可视和不可视的错误”。曾经有个程序员很快写了这样的一段代码给我,说如此简单的代码肯定不会存在什么错误了。
代码确实很简单,但是我看了一下马上给他指出了很多存在的问题。第一个错误,代码中没有看到任何注释性语言,虽然代码简单但还是应该添加注释语言。第二个错误,源文件中没有使用package关键字,可能导致JVM无法正常编译源代码。第三个错误,没有指明System。out。println()方法引用自哪个Java类,虽然JVM在编译的时候会自动添加一些Java基类,但我还是怀疑JVM无法正常编译源代码。第四个错误,书写方式没有符合Java编码规范,所有的类文件开始字母都应该是大写的。第五个错误,书写格式混乱,这样的格式不适合后阶段维护人员的工作。第六个错误……可以这样说“再简单的事物都存在着错误,仔细检查总会发现很多存在的问题”。
有时公司的管理层或者其他部门也不是很明确我们所做的工作,因为客户的指责而把这种愤怒迁于我们,产品经理tyro就和我有过这样的一段对话:
事实上,我们可以从上面的对话中找出许多的错误的根源,这里包含了多种不成熟的反馈。
(1)不成熟的预测:项目在开始之前就要详细描述项目执行和发布后可能遇到的风险,显然我们没有建立良好的风险回避机制。一旦出现了严重的突发事件,我们往往被迫选择这种临时而不负责任的短小会晤。
(2)不成熟的度量:各个控制层面在项目的开始阶段竟然不能做到对各阶段工作量的有效度量,更别说对资源投入的曲线递增计划。我们从来都是羡慕维尔奇在通用公司的成功,可我们从来不去仔细研究这种成功背后的大量实际数据和维尔奇的度量公式。
(3)不成熟的客户:也许我们没有告诉客户一个现实,即使软件已经递交给他们,可是测试工作并没有因此而结束。这同样说明了我们对客户前期。中期培训工作毫无效果,可能公司本身就没有客户培训这种意识。就算项目完全失败了,他们还是一味地责怪自己的团队出现的漏洞足可以开过一辆坦克车。
(4)不成熟的团队:现在软件工程讲究的是团队协作,如果是一个工作状态优秀并且完全投入的团队,就有可能在恶劣的环境中创造出美丽的巴比伦花园。团队是什么,就是既能保存团员个性又能充分体现团队共性的一个战斗体,天生就是为了战胜艰难而存在的。当然,团队和伪团队(小组)是有明显区分的,团队为一件事情的后果承担共同责任,而伪团队则是某一个人为全部的事情负责。
(5)不成熟的交流:A团队不知道B团队究竟在做什么,他们很恼火B团队不能很好地配合他们的工作。同样,B团队也很恼火A团队混乱的工作方向,索性坐下来看一段时间再说。这就是交流和相互扶持的绝对失败,A。B团队在一起的作用已经和他们建立时的初意完全相反了。
(6)不成熟的认识:很多情况下我们总是做事后诸葛亮,以为在已经损坏的项目中继续投入人力也许会完全挽回所有损失。这就是战争,大量的幼稚人群被胡涂的管理层派往战场,可他们会惊奇地发现,事情并非好转而是变得越来越糟糕。
(7)不成熟的我:我不能很好地面对现实,我并没有为自己的错误而羞愧地检讨自己,而是盲目地跟从管理层的意思,眼睁睁地看着事情变得越来越糟糕。就是有太多这样的我,才造成了现在混乱无序的情况。
即使我们做好了所有的事,也不代表我们就作对了所有的事,还需要知道:任何严格的测试都不可能发现软件中存在的所有隐患。做测试之前就需要正确认识这个问题,旮谁能看到自己的背后呢?当然这并不是说我们测试无效,最多是说我们认识不够。所以在测试的事后需要采用更多的发现手段,这里的发现手段就是不同的测试方法。没有一种测试方法可以发现所有问题,然而多种测试方法组合就能发现更多的问题,如图1所示。
不管任何测试方法都有其针对性,类似于图1所示:A。B。C。D四个圆环(代表4种不同测试类型)和一个虚圆环(代表软件中存在的缺陷和错误)交汇,交汇区域代表已经被发现的软件缺陷和错误,也可以称为4种测试类型的有效域。虚圆环的空白代表未发现的软件错误(一个未知的X错误域),4个圆环的空白则表示不同测试类型的失效域(对测试方法的使用不当或者存在陌生操作等原因而造成)。很遗憾,X错误域根本无法度量,就像我们无法预知黑洞的背面一样。但我们必须正视它的存在,尽量提高各种测试类型的有效性,减少X错误。
从事测试工作这么久,我一直想把测试这种抽象的活动用图形表现出来,如图2所示。
图2看上去像个“二极管”电路,其中的单元测试保证程序的内在高品质,功能测试保证程序的外部功能正确。在不断的持续集成的动作中进行强度。性能方面的测试,保证程序中所包含的部件,以及各个部件之间的高稳定性,达到最佳优化状态。最后的配置管理在测试动作后及时更新软件的版本,推动整个产品(或者项目)向良性的道路发展。
刚进入软件测试行业的人总是混淆质量保证和测试,其实这两种行为是不同的。质量保证是全局的,而软件测试隶属于质量保证,是一种积极的实际数据采集活动。换一句话说,测试无法改善软件的质量,它不过是一种事后的监管手段。天下没有不漏水的桶,发现漏水的桶是测试的事情,尽量让生产出来的桶不漏水则是质量保证要做的。
许多新入行的人员总是不停地向别人索取文档(比如测试流程和各种阶段产生的规约),入了行的人员则每天抱怨自己辛辛苦苦做的东西不能很好地用在实际工作中去。测试是一门思想,别人的思想在你这里运用可能是一种灾难,更何况测试是一个整体,需全盘运用。实施测试则是一种策略,不能开始的时候就把所有的事情都做好,需要采用渐进的。迭代的手段去实现,慢慢向别人渗透你的思想。你可以采用如下的改进模型,如图3所示。
初始阶段我们采用一些非常简单的测试方法和模糊的测试过程,也许那个时候的工作输出很糟糕,但不要气馁,应该坚持改进。N+1次修正后,我们清晰了测试过程。N+2次后的修正,我们强化了集成测试和系统测试的部分手段。N+3次后的修正,我们加入了严格的单元测试,并且重新修正了整个测试过程。持续N+I次改进,我们的工作就会渐渐接近完美。走第一步总是最困难的,但却是最值得纪念的,请走出属于你的第一步吧。
本书第4章的敏捷测试过程奉献给所有在测试前沿奋斗的同行们,这个测试过程能让你在资源严重受限的环境中最大限度地保证测试执行质量。其实创造这个测试过程是违心的,一方面它体现了我们低劣的质量管理意识,另一方面它体现了我们当前脆弱的项目管理体系。这里尽可能地把这个过程细化,让你明白每段的实际作用。如果你能把它融入到自己的测试体系中,并且能把使用后的效果反馈给我那就太好了。
书里还包含着我在这个行业的一些经验,当然也有我到今天还无法解答的疑问。我认为每本书的作者都应该积极地和读者们去讨论观点,而不是单纯灌输。写书是为了阐述自己在这个领域里的一些观点和结论,是否正确,则需要大家来评判。我虽然在这本书里使用大量的实际案例,但是我还是要说,这是一本思想书,说明我的测试思想,以及为什么要这样做。你能看出了很多疑问,并产生新的观点,而且渐渐形成自己的测试思想,这才是我写这本书的最终目的。