《Google 软件测试之道》摘录
最近刚刚看完《Google 软件测试之道》,受益颇多,遂记录下:
只有在软件产品变得重要的时候质量才显得重要
第一章:谷歌软件测试介绍
角色介绍
SWE(Software Engineer)、SET(Software Test Engineer)、TE(Test Enginee)
SET也是开发角色
- 100%时间在编写代码,SET和SWE是合作伙伴
- 工作重心在可测试性和通用测试基础框架
- 参与设计评审
- 关注质量提升和测试覆盖率增加
- 写代码的目的是可以让SWE测试自己的功能
- 关注对象是开发人员
TE
- 产品专家,质量顾问和风险分析师
- 大量时间在模拟用户的使用场景上。
- 组织整体质量实践,分析解释测试运行结果,驱动测试执行,构建端到端的自动化测试
- 关注对象是用户
组织结构
测试是独立的部门(工程生产力团队),以租借的方式进入产品
不同项目组的借调,时刻保持新鲜感,也方便好的测试想法快速蔓延。
推广创新技术,直接借调创新的发明者是一个很好的办法。
版本发布
金丝雀版本:每日都要构建的版本,只有这个产品的工程师才会使用。
开发版本:开发人员日常使用的版本,一般每周发布一个。
测试版本:通过了持续测试的版本,基本上是最近一个月内的最佳版本。
beta或发布版本:由稳定的测试版本演变而来,对外发布的第一个版本。
测试类型
Google用小型,中型,大型测试区分测试类型(对应我们的单元,集成,系统测试)
小型测试:函数或模块
小型测试一般需要使用mock和fack,mock对象是指对外面依赖关系的模拟,在运行时刻可以根据假设的需求提供期望的结果,fake对象是一种虚假的实现,内部使用了固定的数据或逻辑,只能返回特定的结果。
中型测试:模块之间的交互
中型测试尝试解决一系列临近的模块互相交互的时候,是否如我们预期的那样工作。
大型测试 :真实用户场景和数据
大型测试尝试去解决的问题是这个产品运行方式是否和用户的期望相同,并产生预期的结果。这种端到端的使用场景以及在整体产品或服务之上的操作行为,即是大型测试关注的重点。
测试的规模越小,就越有可能被实现成为自动化的测试。
Google维护着不同测试类型之间的健康比例,全部使用大型的端到端自动化测试是错误;全部使用小型的单元测试也是错误的 。不同类型的测试都有覆盖率报告(命令行加一个选项即可在浏览器查看覆盖率)
经验法则:70%是小型;20%是中型;10%是大型
如果面向用户,集成度高,用户接口复杂,增加中大型测试比例 ;基础平台或面向数据,增加小型测试比例
第二章:软件测试开发工程师
测试是应用产品的另外一种功能,而SET就是这个功能的负责人
SET审阅设计文档要点:
1)完整性:找出文档中残缺不全或需要特殊背景知识的地方
2)正确性:是否有语法、拼写、标点符号等方面的错误
3)一致性:配图和文字描述一致
4)设计:文档中的一些设计要经过深思熟虑,考虑到可用的资源,目标是否可用顺利达成,要使用何种基础的技术框架
5)接口与协议:是否对所使用的协议有清晰的定义
6)测试:可测试性如何
假如你被要求去实现一个函数account(void *)返回一个字符串中大写字母A出现的次数。如果一上来就直接开始写代码,这无非在传递一个强烈的信息,只有一件事情需要去做而我正在做这个事情,这个事情就是写代码。SET不会遵循这样的世界观,他们会先把问题搞清楚。
这个函数是用来做什么的?我们为什么要构建它?这个函数的原型看起来正确吗?我们期望候选人可以关心函数的正确性以及如何验证期望的行为。
普通的候选人会花几分钟通过提问题和陈述的方式来理解需求文档,例如以下几点。
-传入的字符串编码是什么:ASCII, UTF-8或其他的编码方式?
-函数名字比较槽糕,应该是驼峰式(CamelCased)的?需要更多说明描述,还是这里应该遵循其他的什么命名规范?
-返回值类型是什么(或许面试官忘记了,所以我会增加一个int类型的返回值在函数原型之前)?
-void*是危险的。我们应该考虑更合适的类型,如char*。在一些编译时刻类型检查可以为我们提供一些帮助。
-如果只有一个A的情况,计数结果是多少?它对小写字母a也计数吗?
-在标准库中不是已经有这样的函数了吗(为了面试的目的,假装你是第一个实现这个函数功能的人)?
更好的候选人则会考虑的更多一些。
-考虑下扩展性:或许返回值的类型应该是1个64位的整形,因为Google经常涉及海量数据。
-考虑下复用性:为什么这个函数是针对大写字母A进行计数的?一个好的办法是参数化,使得任意的字符都可以被计数,而不是使用不同的函数来实现。
-考虑下安全性:这此指针都是来自于可信任的地址吗?
最佳的候选人会这样考虑。
-考虑扩展
这个函数会在Shared data(译注:数据分区,是数据库存储分害J( partition)的一种方式。水平分割是一个数据库的设计一准则,数据以记录行的方式存储在不同的物理位置,而不是通过不同列的方式存储。或许这才是调用这个函数最有用的形式。在这个场景需要考虑一些什么问题吗?针对整个互联网的所有文档运行这个函数,该如何考虑性能和正确性?
如果这个子程序被每一个Google查询所调用,而且由于外部的封装层面已经对参数做了验证,传递的指针是安全的,或许减少1个空指针的检查会每天节省上亿次的cpu调用周期,井缩短用户的响应时间。最少要理解全部参数验证所带来的潜在影响。
-考虑基于常量的优化
我们可以假设输入的数据是已经排好顺序的吗?如果是那样,我们或许可以在找到第个大写字母B之后就快速退出。输入的数据是什么结构?多数情况下都是A吗?多数是字符的混合,还是只包含字毋A和空格?如果那样,在我们比较指令的地方或许可以做些优化。当在处理大数据,甚至小数据的时候,在代码执行的时候对于真实的计算延迟也会有比较显著的亚线性变化。
-考虑安全性
在许多系统上,如果这是一段对于安全敏感的代码,可以考虑更多的非空的指针做测试,在某些系统上,1是一个非法指针。
增加一个字符长度的参数,用以保证代码不会运行到指定字符串之外的部分。检查字符串长度,这个参数的值是否正常。那些不是以null结尾的字符串是黑客们的最爱。
如果指针指向的数据能被其他的线程修改,这里就有潜在的线程安全问题。我们是否应该使用try/catch来捕获异常的发生?或者如果未能如预期那样正常的调用代码,我们或许应该返回错误代码给调用者。如果有错误代码的话,这些代码经过良好的定义并有文档吗?这意味着候选人在思考大型代码库和运行时刻的上下文环境方面的问题,这样的思索可以避免错误代码的重复和遗漏。
第三章:测试工程师
TE职责:
测试计划和风险分析
评审需求、设计、代码和测试
探索式测试
用户场景
编写测试用例
执行测试用例(包括提交BUG、跟踪、协助沟通修复以及回归等等)
众包
使用统计(测试每个阶段的版本迭代、BUG数量、质量指标、用时等等数据信息)
用户反馈
假如你被要求测试一个web页面,上面有一个文本输入框,一个计数按钮,用于计算一个文本字符串中大写字母A出现的个数,这里的问题是请设计出一系列字符串用以测试这个web页面
如果一头扎进去开始罗列测试用例,这往往是一个危险的信号,说明他们还没有充分思考这个问题,追求数量而非质量的倾向是一种低效的工作方式。更好的是会提出一些问题,做进一步需求的澄清:
大写还是小写?只是英语吗?计算完成后文本会被清除吗?多次按下按钮会发生什么?诸如此类
典型的测试用例列表如下:
-"banana":3(一个合法的英文单词)
-"A"和"a":1(一个简单的有正常结果的合法输入)
-"":0(结果为0的合法输入)
-null:(简单的错误输入)
-"b":0(一个简单的非空合法输入)
-"aba":2(目前字符出现在开头和结尾,以寻找循环边界错误)
-"bab":1(目标字符出现在中间)
-space/tabs等:N(空白字符与N个A的混合)
-不包含A的长字符串:N,其中N>0
-包含A的长字符串:N,其中N是A出现的个数
-X\nX字符串:N,其中N是A出现的个数(格式化字符)
-{Java/C/HTML/JavaScript}:N,其中N是A出现的个数(可执行字符,或错误,或偶然的代码解释)
更好的候选人会超越输入选择,讨论更高级的测试问题:
质疑界面的外观、调色板和对比度,如"这些与相关的应用风格一致吗?","视力困难的人能使用吗?"等等
担心文本框太小,建议加长以便显示更长的输入字符串
考虑这个应用能否在同一台服务器上运行多个实例,会发生多个用户的串扰吗?
提出疑问"数据会被记录吗",输入串困难包含地址和其他身份信息
建议使用真实的数据进行自动化测试,如从词典和书本里选择
提出疑问,"计算机足够快吗?在大负载下呢?"
提出疑问,"该页是可发现的吗?用户怎么能找到该页面?"
输入HTML和JavaScript,看是否破坏页面渲染
询问是对大写还是小写的A计数,还是都包括
尝试复制和粘贴字符串
意识到计算会通过URL-Encode HTTP GET请求传递给服务器,字符串可能会在穿越网络时被截断,因此无法保证支持多长的URL
建议将此应用参数化,为何只对字母A计数呢?
考虑计算其他语音中的A(如变音符号)
考虑该应用是否可以被国际化
考虑编写脚本或手工采样来探知字符串长度的上限(如采用2的指数递进算法),然后确保在此区间内功能正常
考虑背后的实现和代码。也许有一个计数器遍历该字符串,另一个跟踪已经遇到了多少个A(累加器),因此可以在边界值附近变化A的个数和字符串的长度来进行测试
提出疑问,"HTTP POST方法和参数会被黑掉吗?也许有安全漏洞?"
用脚本创建各种有趣的排列组合和字符串特性如长度、A的个数等的组合,自动生成测试用例输入和验证。
意识到系统可能是有状态的,测试必须将先前的输入考虑在内。因此多次输入同一字符串,或者在长度为1000的字符串之后输入一个长度为0的。
ACC(Attribute Componet Capability,特质、组件、能力)
Google使用ACC作为一种测试计划的替代方法。
-A代表特质:
代表产品的品质和特色,是区别竞争对手的关键。产品经理或者开发人员已经确定了产品的特质,测试人员只需将特质记录下来,以备使用。
-C代表组件:
组件是构成待建系统的模块。组件容易识别,经常出现在设计文档中。
-C代表能力:
组件(Componet)执行某种功能(function)来满足产品的一个特质(Attribute),这个活动的结果是向用户提供某种能力(Capability)。
对于特质和组件来说,花几分钟的时间来理清就足够了。能力最重要的一个特点是它的可测试性。TE需要将能力转化为测试用例。
风险分析
-哪些事件需要担心
-这些事件发生的可能性有多大?
-一旦发生,对公司产生多大影响?
-一旦发生,对客户产生多大影响?
-产品具备什么缓解措施?
-这些缓解措施有多大可能会失败?
-处理这些失败的成本有哪些?
-恢复过程有多困难?
-事件是一次性问题,还是会再次发生?
影响风险的因素很多,关键性的有两个因素:失败频率(frequency of failure)和影响(impact);风险是一个定性的相对值,而不是一个定量的绝对值。
第四章:测试工程经理
略
第五章:Google软件测试改进
Google流程中的致命缺陷
第一个致命缺陷:测试成了开发的拐杖。
我们越不让开发考虑测试的问题,把测试变得简单,开发就越来越不会去做测试。
第二个致命缺陷:开发与测试的组织结构分离。
测试人员更关注自己的角色,而不是他们的产品。健康的组织的一个标志是,人们会说“我在为Chrome工作”,而不是“我是测试”。任何角色都不应该被过分强调。团队的每个人都是在为产品工作,而不是为了开发过程中的某个部分。
第三个致命缺陷:测试人员往往崇拜测试产物胜过软件本身。
测试的价值在于测试的动作,而不是测试产物。测试人员必须把产品放在第一位。
第四个致命缺陷:产品经过最严格的测试发布后,用户有多大可能仍然发现测试中遗漏的问题?几乎必然发现。
SET的未来
作者认为SET没有未来。
SET就是开发,与开发的待遇一致。测试特性应该由团队的新成员负责,特别是那些资历尚浅的员工。
TE的未来
TE的需求量会越来越少。
测试工程会转型成测试设计。少量的测试设计师快速地规划出测试范围、风险热图和应用程序的漫游路线。可以识别需要专业技能的地方,比如安全性、隐私、性能和探索式测试。
测试总监和经理的未来
数量大幅减少。他们将作为思想领袖,为维系松散的测试工程师和负责质量的软件工程师的关系而存在,但不会最终为某个特别项目的质量或管理负责。
未来的测试基础设施
目前Google的测试基础设施是基于客户端的,在测试创建和执行上花费昂贵的人工和机器建设成本。
测试基础设施会最终整体迁移到云端,使用更加开放、基于云计算的方式。测试用例库、测试代码的编辑、录制和执行等都将在一个网站或通过浏览器插件完成。
备注:
本书花了大量篇幅记录优秀SET、TE、测试工程经理的访谈,开放性的问答,尊重个性也注重创新,很值得学习。