Google 软件测试之道 (3)
Posted on Wed, 25 Dec 2024 10:37:29 +0800 by LiangMingJian
Google 软件测试之道 (3)
测试工程师
测试工程师(Test Engineer,TE)的工作在于评估软件产品对用户的影响以及整体目标上的风险。因此,当TE进入产品的时候,需要考虑以下一些问题。
- 当前软件的薄弱点在哪里?
- 有没有安全、隐私、性能、可靠性、可用性、兼容性、全球化和其他方面的问题?
- 主要用户场景是否功能正常?对于全世界不同国家的用户都是这样么?
- 这个产品能与其他产品(软件和硬件)互操作吗?
- 当发生问题的时候,是否容易诊断问题所在?
这些东西加起来,构成发布待评估软件的风险概要。TE并不需要自己去解决所有这些问题,但必须保证这些问题被解决掉。TE的根本使命是保护用户和业务的利益,使之不受到糟糕的设计、令人困惑的用户体验、功能bug、安全和隐私等问题的困扰。
显然,在不同的项目中,TE的工作内容也会有较大的不同。一些TE会在编码方面投入较多的时间,但主要是写中到大型的测试(如端到端的用户场景)而非小型测试。其他一些TE会检查代码和系统设计以确定失效模式,并寻找导致失效的错误路径。在这种情况下,TE可能会去修改代码,但这与从头编写代码是不同的。TE在测试计划及测试完整性上必须更加系统和周密,重点在真实用户的使用方式和系统级别的体验上。TE擅长发现需求中的模糊之处,分析沟通不明确的问题。
成功的TE游走于这些微妙且敏感的地方,有时候还要与个性很强的开发和产品人员打交道。一旦找到薄弱点,TE就会通过测试使软件出错,然后与开发、产品一起推动解决这些bug。
TE的工作经常需要去打破常规流程。TE可以在任何时间进入项目,必须迅速评估项目、代码、设计和用户的当前状态,然后决定首要的关注点。如果项目刚刚开始,测试计划是第一优先级。有时,TE在产品后期被拉进来帮助评估项目是否可以发布,或者在beta版本发布之前确认还有哪些主要的问题。当TE进入了一个新被收购的应用或缺少相关应用经验的时候,他们经常会先去做一些不怎么需要计划的探索式测试。有时,项目已经很久没有发布了,只是需要去做一些修饰、安全补丁或界面更新,这需要迥然不同的方法。
下面是Google关于TE职责的一般性描述。
- 测试计划和风险分析;
- 评审需求、设计、代码和测试;
- 探索式测试;
- 用户场景;
- 编写测试用例;
- 执行测试用例;
- 众包(译注:crowdsourcing,是互联网带来的新的生产组织形式。一个公司或机构把过去由员工执行的工作任务,以自由自愿的形式外包给非特定的(通常是大型的)大众网络的做法);
- 使用统计;
- 用户反馈。
测试计划
和测试人员相比,开发人员有一个优势就是他们的工作产物是每个人都真正关心的。开发人员编写代码,构建用户期望的、能为公司赚钱的应用。很明显,代码是项目过程中产生的最重要的文档。然而,测试人员要处理的是真正的文档和其他临时性的事物。在项目的早期阶段,测试人员编写测试计划;然后,他们创建和执行测试用例,编写bug报告;接下来是准备覆盖度报告,收集用户满意度和软件质量数据。在软件成功发布(或失败)之后,很少有人会问及测试产物是什么。如果软件深受人们喜爱,大家就会认为测试所作所为是理所应当的;如果软件很糟糕,人们可能就会质疑测试工作。但其实也没人真正想去了解测试到底做了什么。
测试人员不应该对测试文档过于珍爱。软件开发过程充满了痛苦的挣扎:编码、评审、构建、测试、一轮接一轮的开发等,在这个过程里实在很难有时间坐下来欣赏一下测试计划。糟糕的测试用例不会受到足够的关注而被改善,它们只会被抛弃。则被留下来的是更好地测试用例。大家的关注点集中在不断增长的代码库,这才是最重要的东西,理应如此。
作为一种测试文档,测试计划的生命周期是所有测试产物中最短的(显然,当客户明确要求编写测试计划,或者出于某些政府法规要求,就没这么灵活了。某些场合必须有测试计划并且保持更新)。在项目早期,人们需要一个测试计划。事实上,项目经理经常坚持必须有一个测试计划,并将编写测试计划作为一个比较重要的里程碑。但是,一旦计划就绪,这些人就把它扔到一边了,既不评审也不更新。测试计划就像是闹脾气的小孩儿手中可爱的毛绒玩具。我们希望它总是存在,到哪里都能带着它,但却从不真正关注它。只有它被拿走的时候,我们才会发出尖叫。
测试计划是最早出现、最先被遗忘的测试产物。在项目早期,测试计划代表了对软件功能的预期。但是,除非得到持续的关注,它会很快随着新代码的完成、功能特性的改变以及设计的调整而过期。伴随着计划内或计划外的变更,维护一份测试计划是要花费大量精力的,除非多数项目的成员会定期查看,否则测试计划并没有什么价值。
后面这一点才是测试计划真正的杀手:试问在产品的整个生命周期中,测试计划能在多大程度上作为测试活动的指导?测试人员会不断参考计划来安排一个应用的测试吗?会要求开发人员在功能增加或修改时去更新测试计划吗?在开发经理管理 to-do 列表的时候,他们会在桌面上打开一份测试计划吗?在进展沟通会议上,测试经理会经常参考测试计划的内容吗?如果测试计划真的重要,那么所有这些事情应该每天都会发生。理想情况下,测试计划应当在项目执行中发挥核心作用,应当在软件的整个生命周期中持续有效:随着代码库的更新而更新,时刻代表最新的产品功能,而不是停留在项目开始阶段时的样子。它应该可以帮助一个新加入的工程师迅速跟上项目进展。
但是,这些不过都是理想情况而已。在Google或其他公司中,其实很少有测试人员能真正做到。下面是我们希望测试计划具有的一些特性。
- 及时地更新。
- 描述了软件的目标和卖点。
- 描述了软件的结构、各种组件和功能特性的名称。
- 描述了软件的功能和操作简介。
- 从纯粹测试的角度看,我们担心的是测试计划的投入和价值产出是否匹配。
- 不必花过多的时间去撰写,必须随时可以被修改。
- 应该描述必测点。
- 应该能在测试中提供有用的信息,从而帮助确定进展以及覆盖率上的不足。
在Google,测试计划的历史与我们所经历的其他公司基本相同。测试计划曾经是由各团队根据自身的实际情况自行定义和执行的。一些团队用文本文档和电子表格编写测试计划,与整个工程团队分享,但不放在中心数据库里;一些团队将测试计划放到产品主页的链接里;一些团队则放到项目的内部 Google Sites 页面里,或者作为工程设计文档或内部 wikis 的链接;少数团队甚至使用 Microsoft Word 文档,通过电子邮件传播;一些团队完全没有测试计划。我们只能认为测试用例的总数代表了整个测试计划。
这些测试计划的评审链条是不透明的,很难确定作者和评审者。相当多的测试计划有一个时间和日期戳,非常清楚地表明了他们悠长的被遗忘的历史,就像冰箱角落里酱罐的保质期一样。它一定在某个时间对某个人发挥了重要的作用,但那个时间已经一去不返了。
ACC
ACC(Attribute Component Capability,即特质、组件、能力。这是一种测试计划的替代方法),ACC的指导原则如下。
- 避免散漫的文字,推荐使用简明的列表。并不是所有的测试人员都想当小说家,也不具备将一个产品的目标或测试需求表达成散文的技能。而且,冗词赘句容易误读,只列出要点和事实就行了。
- 不必推销。测试计划不是营销文案,既不是要讨论一个产品满足了多么重要的市场定位,也不是讨论这个产品有多么酷的功能。测试计划不是给客户或分析师看的,它的受众人群是工程师。
- 简洁。测试计划并没有长度的要求。它不是中学的项目作业,长度无关紧要,不是越长越好。计划的大小与测试问题的规模有关,与作者的写作欲望无关。
- 不要把不重要的、无法执行的东西放进测试计划。相关人员毫不关心的东西,就一个词也不要出现。
- 渐进式的描述(Make it flow)。测试计划的每个部分应该是前面部分的延伸,以便读者可以随时停止阅读并且对产品的功能有一个初步的印象。如果读者希望了解更多的细节,那么他可以继续读下去。
- 指导计划者的思路。一个好的计划过程能帮助计划者思考产品功能及其测试需求,从而有条不紊地从高层概念过渡到可以被直接实现的低层细节。
- 最终结果应该是测试用例。在计划完成的时候,它不仅要清楚地描述要做什么样的测试,并且还可以清楚地指导测试用例的编写。做出一个不直接指导测试的计划纯粹是在浪费时间。
- 最后一点非常重要:如果测试计划没有把测试用例应该怎么执行描述得足够详细,它就没有达到预先设定的帮助测试的本义。对测试的计划(the planning of tests)而言,它显然应该让我们清楚地知道需要编写哪些测试用例。当你正好处于"完全了解需要编写哪些测试"这一点时,才算完成了测试计划。
ACC通过指导计划者依次考察产品的三个维度达成这个目标:描述产品目标的形容词和副词;确定产品各部分、各特性的名词;描述产品实际做什么的动词。这样,我们通过测试完成的就是验证这些能力(capabilities)能正常运作、产品各组件(component)能满足应用的目标。
A代表特质(Attribute)
在开始测试计划或做ACC分析的时候,必须先确定该产品对用户、对业务的意义。我们为什么要开发这个东西呢?它能带来什么核心价值?它又靠什么来吸引用户?记住,我们既不需要为这些问题做辩护,也不需要做什么解释,只要写下来就行了。我们可以假定产品经理和做产品计划的人,或者开发人员已经在这方面做了该做的事情。从测试的角度看,我们只需要确定并记下来,以备后续测试使用即可。
我们通过一个称为特质、组件、能力分析的过程来记录这些核心价值。
特质是系统的形容词,代表了产品的品质和特色,是区别于竞争对手的关键。在某种程度上,是人们选择你的产品而不是竞争对手的产品的原因。例如,Chrome的定位是快速、安全、稳定和优雅,这正是我们通过ACC记录的特质。之后,我们希望能够将测试用例关联到这些标签,这样,我们就会知道在验证Chrome的快速、安全等特质方面已经完成了多少测试。
一般来说,产品经理会整理一个系统特质的列表,测试人员通过阅读产品需求文档、团队愿景和使命声明,甚至是听销售跟潜在的客户描绘这个系统来确定这个列表。说真的,我们发现在Google里,推销员和产品传道士是极佳的特质来源。想象一下箱体广告或你的产品将要如何在QVC(译注:QVC是全球最大的电视与网络的百货零售商,含义是Quality质量、Value价值、Convenience便利,通过电视与网络购物服务直达美国8,000万户以上的家庭)上做宣传,你就会找到列出这些特质的感觉了。
下面是一些小窍门,可以帮助你在自己的项目里确定产品特质列表。
- 简单。如果1~2个小时还没有完成,那和你在这一步花的时间太多了。
- 精确。确保它来自于团队已经普遍认同的文档或营销信息。
- 变化。不必担心您是否漏掉了什么–如果后来发现这个特质不明显,极有可能它也不怎么重要。
- 短小。数量方面,一打(十二个)是一个不错的目标。
使用特质的目的,是确定哪些特性是产品存在的根本原因,并使这些原因为测试人员所周知。这样,他们就会意识到自己所做的测试是如何对产品存在的根本原因产生影响的。拿Google Sites这个产品来举个例子。这是一个免费的应用,供开放或封闭的社区建立自己的共享网站。Sites类似许多终端应用,在它的文档里描述了大多数的特质。实际上,大多数应用程序具有类似的开始页面或销售材料,这经常可以帮你确定特质列表。如果没有,那就找一个销售聊一聊,或者采用更好的方式(如参加一个销售电话或演示),就可以得到所需信息了。特质就在那里等着你。如果你不能在几分钟内列举出来,说明你还没有足够的理解你的产品,还不能有效地测试它。一旦熟悉了你的产品,罗列特质不过是几分钟的事情。
C代表组件(component)
组件是系统的名词,在特质被识别之后确定。组件是构成待建系统的模块,例如在线商店的购物车和结账系统,word处理器的格式化和打印功能等。组件是使一个软件之所以如此的关键代码块。实际上,他们正是测试人员要测试的对象。
一般来说,组件容易识别,经常出现在设计文档里。对大型系统来说,它们是架构图里的大框架,经常出现在bug库中的标签里,或者在项目主页和文档中被高亮出来。对小型项目来说,它们是代码里的类和对象。无论何时,只要问一下开发人员"你们在编写什么组件",你就可以毫不费力地得到一个列表。
与特质一样,在识别组件时,到达何种级别的细致程度是至关重要的。太多的细节除了把人搞晕之外不会再有什么好处,而太少的细节也会导致无物可测。确保一个短小的列表:10看起来不错,20就太多了,当然除非系统非常大。把一些次要的东西排除在外,是可以的。既然是次要的,那它们或是另外一个组件的一部分,又或者对于最终用户而言都无关紧要,不值得在上面花精力。
事实上,对于特质和组件来说,用几分钟的时间来理清它们就足够了。如果你费了很大劲来确定这些组件,那说明你对产品缺乏了解,你应该花一些时间来使用它直到成为高级用户。任何高级用户都应该能够立即罗列出特质列表,任何对源代码和文档有访问权限的项目内部人员也应该能够迅速地列出它的组件。毫无疑问,我们认为很重要的一点是,测试人员既是高级用户,也是项目内部人员。
最后,不必担心完整性问题。整个ACC过程的要点是快速行动,动态迭代。漏掉的特质可以在罗列组件时被发现。当你开始做"能力"部分的时候,你也会找到那些先前遗漏的特质或组件。
C代表能力(capability)
能力是系统的动词,代表着系统在用户指令之下完成的动作。它们是对输入的响应、对查询的应答,以及代表用户完成的活动。事实上,这正是用户选择一个软件的原因所在:他们需要一些功能而你的软件提供了这些功能。
例如,Chrome具有渲染Web页面和播放Flash文件的能力,可以同步多个客户端,下载文档。所有这些都是能力,再加上许多其他的功能,构成了Chrome web浏览器的完整能力集合。另一方面,一个购物应用具有商品搜索和完成一笔交易的能力。当一个应用能够完成一个任务的时候,这个任务就被标记为它的一项能力。
能力处于特质和组件的交点。组件(component)执行某种功能(function)来满足产品的一个特质(attribute),这个活动的结果是向用户提供某种能力(capability)。Chrome飞快地渲染一个页面。Chrome安全地播放一个Flash文件。如果你的产品所做的一件事情不属于任何特质和组件的交点,这件事大概也是无关紧要的,而且还会让人产生疑问:为什么要实现这样的功能呢?如果一个功能不能为产品带来核心价值,就像是可以被去掉的肥肉一样,那么这个功能也就无甚益处,反而可能会带来不少毛病。事实或者如此,或者是有合理的解释但你却不知道。“不懂产品"是测试这个职业所不可接受的。任何工程师,如果理解了产品的用户价值,他就可以成为一名测试人员。
这里是一个例子,展示了一个在线商店具有的能力。
- 从购物车里增加或删除物品。这是Cart(购物车)组件在满足直观的UI(Intuitive UI)特质时的一个能力。
- 获得信用卡和验证数据。这是Cart组件在满足便利(convenient)特质和集成(Integrated)特质(如与支付系统集成)时的一个能力。
- 使用HTTPS处理钱款交易。这是Cart组件在满足安全(secure)特质时的一个能力。
- 基于购物者正在浏览的商品提供建议。这是Search组件在满足便利(convenient)特质时的一个能力。
- 计算送货成本。这是UPS集成组件在满足快速(fast)和安全(secure)特质时的一个能力。
- 显示剩余库存。这是Search组件在满足便利(convenient)和精准(accurate)特质时的一个能力。
- 推迟购买。这是Cart组件在满足便利(convenient)特质时的一个能力。
- 根据关键字、SKU和类目搜索商品。这是Search组件在满足便利(convenient)和精准(accurate)特质时的一个能力。一般情况下,我们倾向于把每一种搜索当作一个单独的能力。
显然你会发现大量的能力。当你感到正在列出所有可测之处的时候,说明你已经掌握了ACC的精髓,那就是快速简明的列出保证待验证系统能正常运转的那些最重要的能力。能力一般是面向用户的,表达的是用户眼里系统的行为,往往比特质和组件都要多很多。ACC的前两步遵循简洁法则,而能力则应当描述系统的完整功能,因此基于应用的功能丰富性和复杂性,能力在数量上可以很大。
就我们在Google 涉及的系统而言,大型复杂应用拥有成百上千个能力(例如,Chrome OS有300多项能力),而较小的应用则有数十个能力。当然,只有几个能力的产品也是有的,往往只需要开发人员自己或少数早期用户做一些测试就行了。因此,当所测产品的能力少于20个时,可能需要反思一下自己在这个项目中的意义。
能力最重要的一个特点是它的可测试性。这是我们用主动语态来表达能力的主要原因。它们是动词,因为我们为了完成某个动作,我们不得不编写测试用例去验证这个能力得到了正确的实现,而用户将因为这个特性而喜欢这个产品。后面我们将讨论如何把能力转换成测试用例。
在罗列能力时,应该达到什么样的抽象级别呢?这在Google TE中存在很大的争议。依其定义,能力不是原子动作。一个能力可以描述任意数量的用例。在之前描述在线商店的例子中,能力描述并没有限定购物车中的商品或一个搜索的结果,而只是表达了用户可能会做的事情。这是有意的,因为太多的细节会导致长篇大论。穷尽所有可能的搜索和购物车配置来完成测试是不可能的。因此,我们在把能力转换成测试用例的时候,只会重点考察那些实际使用的测试场景。
能力描述并不是测试用例,不会包含实际测试所需要的一切信息,例如特定的值和具体的数据。能力只要说明用户可以购物,而测试用例则要指定他们买什么东西。能力是软件可以提供或者用户可能要求的动作的一般性概念,是抽象的,测试和价值隐含其中,但它们不是测试本身。
还是以Google Sites为例,给出一个以特质为x轴,组件为y轴的表格。通过这种方法,能力被映射到特质和组件。首先,注意大量的单元格是空的。这很正常,因为不是每个组件对每个特质都有影响。对Chrome来说,只有一部分组件对快速或安全性负责;而其他组件对这些特质却没有影响,对应的单元格就为空。空单元格表示我们不必测试这个特定的特质组件对。
能力表的每一行或列表示按某种方式相关联的一个功能切片,是将应用功能分解为多个可测试的活动的一个好办法。测试经理可以把每一行分给一个测试小组,或者针对一行或一列进行深度的bug大扫除。行或列也是探索式测试的极好目标,每个探索式测试人员负责不同的行和列,就可以有效避免重叠,并达到更高的覆盖度。
单元格中的数字表示该组件满足此特质的能力的数量。因此,这个数越大,该交叉点需要的测试点就越多。例如,Page View组件有3个能力影响到Sharing这个特质。
- 协作者都有权限访问相关文档。
- 与另外一个协作者分担页面管理责任。
- 查看一个页面中协作者的位置。
这些能力点可以很方便地指定Page View / Sharing这个组件/特质对需要的测试。我们可以直接为这些能力点编写测试用例,或者将它们组合成更大的用例或测试场景来测试能力的组合。书写良好的能力需要一些训练。下面是一些能力应该满足的特性以供参考。
- (1)一个能力点应当被表达为一个动作,反映了用户使用被测应用完成一定的活动。
- (2)一个能力点应当为测试人员提供足够的指导,用以理解在编写测试用例时涉及到的变量。例如,使用https处理钱款交易这个能力,需要测试人员理解系统支持何种类型的钱款交易、如何验证交易是通过https进行的。显然,这里有很多工作要做。如果某些钱款交易有被遗漏的可能(如被某个测试新人),那么就一定要把这个能力复制多份,以便能明确的展示各种交易类型。如果不会发生遗漏,原来的抽象程度就足够了。同样的,如果https是大家都理解的东西,那这个词无需额外解释。千万不要掉进把一切东西都当作能力记录下来的陷阱。能力应当是抽象的,把更多的细节留给测试用例或者探索式测试吧(将这些细节留给测试人员,为从不同角度理解能力和编写测试用例留下了自由发挥的空间,这有助于提高测试的覆盖度)。
- (3)一个能力应当与其他能力组合。实际上,一个用户故事或用例(或你选择的其他术语)可以用一系列能力来描述。如果一个用户故事无法用现有的能力来表达,那说明你遗漏了一些能力,或者能力描述的抽象程度太高了。
用一系列能力来描述用户故事,这个中间步骤可以为测试带来更大的灵活性。事实上,在Google有几个团队,在与外包接洽或者组织众包型的探索式测试时,更愿意使用比较一般性的用户故事,而不是太细节的测试用例。很细致的测试用例反而会导致外包人员在一遍又一遍的重复执行时产生厌倦感,而用户故事则为确定具体行为留出了更大的余地,从而使得测试更加有趣,较少因为枯燥、死板地执行导致产生错误。不管最终目标是用户故事、测试用例还是两者兼有,这里是一些从能力到测试用例的一般性指南。记住这些只是目标,而非绝对标准。
- 每个能力都应该链接到至少一个测试用例。如果能力有足够的重要性被记录下来,也应该有足够的重要性被测试。
- 很多能力需要多个测试用例。每当输入、输入顺序、系统变量、使用的数据等存在变化的时候,就需要编写多个测试用例。How to break software一书中提及的攻击,Exploratory Software Testing一书中提及的漫游,都可用来指导测试用例的选择或思考哪些数据和输入最有可能发现一个bug。
- 并非所有的能力都是同等重要的。流程的下一步(在下一节中描述)讨论通过关联风险来区分能力的重要性这一问题。