书籍节选:探索性软件测试

作者:LJ Staff

本章节选自 James Whittaker 著作的探索性软件测试:指导测试设计的技巧、诀窍、游览和技术,由 Addison-Wesley Professional 出版社于 2009 年 8 月出版ISBN 0321636414,版权 2010 Pearson Education, Inc. 欲了解更多信息,请访问出版社页面:www.informit.com/title/0321636414

第 2 章:手动测试的理由

“编写无错误程序有两种方法;只有第三种方法有效。”

Alan J. Perlis

软件缺陷的起源

软件缺陷的起源始于软件开发本身的起源。 显然,我们并非一开始就拥有完美的软件,然后才发明出搞砸它的方法。1 事实上,“缺陷”一词自该学科创立之初2就已在软件开发中普遍使用,并且是当今在每个办公室、车库、宿舍、数据中心、实验室、卧室、咖啡馆以及其他任何开发软件的地方使用的术语。 第一个软件有缺陷,最新的软件也有缺陷,介于两者之间的所有比特和字节也是如此。 软件现在不是,而且可能永远不会是,没有缺陷的。

有趣的是,霍珀的飞蛾(参见第二个脚注)实际上并非程序员创建的缺陷。 相反,这是一个操作风险,开发人员在设计时没有考虑到。 正如我们将在后面的章节中看到的,开发人员未能理解、预测和测试潜在的操作环境仍然是软件故障的主要来源。 不幸的是,答案比关上窗户以防止飞蛾飞入要复杂得多。 但我们不要超前。 在本书中,我们将讨论程序员创建的缺陷以及通过操作环境潜入的缺陷。

预防和检测缺陷

鉴于故障不可避免,讨论各种将缺陷排除在软件生态系统之外的方法似乎是合适的,以便最大限度地减少故障并产生尽可能最好的软件。 实际上,此类技术主要分为两大类:缺陷预防和缺陷检测。

预防缺陷

缺陷预防技术通常以开发人员为导向,包括编写更好的规范、执行代码审查、运行静态分析工具以及执行单元测试(通常是自动化的)。 所有这些预防技术都存在一些根本问题,这些问题限制了它们的功效,如下面的小节所述。

“开发人员是最糟糕的测试人员”问题

开发人员可以在自己的代码中找到缺陷的想法是值得怀疑的。 如果他们擅长查找缺陷,难道他们不应该一开始就知道不要编写缺陷吗? 开发人员有盲点,因为他们从构建应用程序的角度进行开发。 这就是为什么大多数关心优秀软件的组织都会聘请第二批人来测试它。 简直没有什么比没有开发偏差的全新视角更能检测缺陷了。 并且没有什么可以替代测试人员的态度,即我如何才能破坏它来补充开发人员的态度,即我如何才能构建它

这并不是说开发人员应该完全不做测试。 测试驱动开发或 TDD 显然是一项旨在作为开发练习的任务,我非常相信由原始开发人员完成的单元测试。 在软件仍在开发中时,需要捕获任意数量的格式、数据有效性和异常情况。 然而,由于前面所述的原因,对于更微妙的问题,需要第二批人来发现,否则这些问题可能会等待用户偶然发现。

“静态软件”问题

任何不需要软件实际运行的技术,例如代码审查或静态分析,都必然会分析静态软件。 一般来说,这意味着基于分析源代码、目标代码或已编译的二进制文件或程序集内容的技术。 不幸的是,许多缺陷直到软件在实际操作环境中运行时才会浮出水面。 第 1 章“高质量软件的理由”中先前显示的大多数缺陷都是如此:除非您运行软件并为其提供真实输入,否则这些缺陷将仍然隐藏。

“无数据”问题

软件需要输入数据才能执行其无数的代码路径。 实际执行哪些代码路径取决于应用的输入、软件的内部状态(存储在内部数据结构和变量中的值)以及外部影响,例如数据库和数据文件。 通常,随着时间的推移,数据的积累会导致软件故障。 这个简单的事实限制了开发人员测试的范围,而开发人员测试的持续时间往往很短。

也许有一天会出现工具和技术,使开发人员能够编写代码而不会引入缺陷。3 诚然,对于诸如缓冲区溢出4等狭义的缺陷类别,开发人员技术可以并且已经将它们推向接近灭绝的境地。 如果这种趋势继续下去,那么对大量测试的需求将会消失。 但是,在我看来,我们距离实现这一梦想还有很长的路要走,需要几十年。 在那之前,我们需要第二批人,在类似于实际使用的环境中运行软件,并使用像真实用户数据一样丰富的数据。

谁来提供这第二批人? 软件测试人员提供这项服务,他们使用技术来检测缺陷,然后熟练地报告缺陷,以便修复缺陷。 这是一个动态过程,在不同的环境中执行软件,使用真实的数据,并在测试发生的短周期内尽可能多地管理输入变化。 这就是软件测试人员的领域。

检测缺陷

测试人员通常使用两种形式的动态分析:自动化测试(编写测试代码来测试应用程序)和手动测试(使用交付的用户界面手动应用输入)。

自动化测试既有污名又有尊重。

污名来自于测试是代码这一事实,而编写测试意味着测试人员必然也是开发人员。 开发人员真的能成为一名优秀的测试人员吗? 许多人可以,许多人不能,但测试自动化中的缺陷经常发生这一事实意味着他们将花费大量时间编写代码、调试代码和重写代码。 一旦测试成为一个开发项目,人们不禁要问,测试人员花费多少时间思考测试软件,而不是维护测试自动化。 不难想象会偏向后者。

尊重来自于自动化很酷这一事实。 人们可以编写一个程序,该程序将执行无限数量的测试并找到大量缺陷。 当应用程序代码被修改或需要回归测试时,可以运行然后重新运行自动化测试。 精彩! 杰出! 我们必须多么崇拜这种自动化! 如果以测试人员运行的测试数量来评判他们,那么自动化将始终获胜。 如果以他们运行的测试的质量来评判他们,那就是完全不同的事情了。

关键是我们已经自动化了多年,甚至几十年,但我们仍然生产出当它出现在真实用户的桌面上时很容易崩溃的软件。 为什么? 因为自动化遭受了与其他形式的开发人员测试相同的许多问题:它是在实验室环境中运行的,而不是在真实用户环境中运行的,而且我们很少冒险让自动化与真实的客户数据库一起工作,因为自动化通常不是很可靠。 (毕竟它是软件。)想象一下自动化会添加和删除数据库的记录——哪个头脑正常的客户会允许这种自动化接近他们的真实数据库? 并且自动化测试有一个阿喀琉斯之踵,没有人解决过:oracle 问题。

oracle 问题是测试中最大挑战之一的一个好听的名字:当我们运行给定的测试用例时,我们如何知道软件是否完成了它应该做的事情? 它是否产生了正确的输出? 它是否在没有不必要的副作用的情况下做到这一点? 我们如何才能确定? 是否有一个 oracle 我们可以咨询,它会告诉我们——给定用户环境、数据配置和输入序列——软件是否完全按照设计执行? 鉴于规范不完善(或不存在)的现实,这对于现代软件测试人员来说根本不是现实。

如果没有 oracle,测试自动化只能发现最严重的故障:崩溃、挂起(可能)和异常。 自动化本身就是软件这一事实通常意味着崩溃发生在测试用例中而不是软件中! 微妙/复杂的故障会被遗漏。 人们只需回顾前一章就可以看到,许多此类关键故障很容易滑入已发布的代码中。 自动化很重要,但它还不够,过度依赖它可能会危及产品在现场的成功。

那么,这让测试人员处境如何呢? 如果测试人员不能依赖开发人员的缺陷预防或自动化,她应该把希望寄托在哪里? 唯一的答案只能是手动测试。

手动测试

手动测试是人工参与的测试。 人工测试人员使用他们的大脑、手指和智慧来创建导致软件失败或完成其任务的场景。 人工参与的测试提供了创建真实用户场景的最佳机会,在真实用户环境中使用真实用户数据,并且仍然可以识别明显和微妙的缺陷。

手动测试是查找与应用程序底层业务逻辑相关的缺陷的最佳选择。 业务逻辑是实现用户需求的代码; 换句话说,它是客户购买软件的代码。 业务逻辑很复杂,需要人工参与来验证其正确性,而自动化通常不适合完成这项任务。

也许以开发人员为导向的技术会发展到不再需要测试人员的地步。 事实上,对于软件生产者和软件用户来说,这都是一个理想的未来,但在可预见的未来,基于测试人员的检测是我们找到重要缺陷的最佳希望。 自动化要跟踪所有变化、场景和可能的故障实在是太多了。 它需要“人脑参与”。 本十年、下一个十年,甚至再往后的几年,情况都是如此。

我希望事情就这么简单,但从历史上看,该行业并不擅长手动测试。 它太慢、太临时、不可重复、不可重现、不可转移,而且没有足够的好的建议供测试人员掌握它。 这使得手动测试的名声很差,被视为开发的丑小鸭。 不幸的是,情况就是这样,但这就是我们面临的困境。

现在是我们将可用的最佳技术应用于手动测试过程的时候了。 这就是本书探讨的探索性测试的主题。 我希望该行业摆脱临时手动测试的想法,并朝着更有目的性和指导性的探索性测试过程努力。 它应该是一个手动测试需要仔细准备,但又为测试期间的智能决策留有余地的过程。 手动测试太重要了,不能对其给予任何不尊重的待遇。

我们可能会展望软件只需工作的未来,但如果我们实现这一愿景,那将是当今手动测试人员的辛勤工作使其成为可能。

脚本式手动测试

许多手动测试人员都遵循预先编写的脚本,这些脚本指导输入选择并指示如何检查软件结果的正确性。 有时脚本是特定的:输入此值,按下此按钮,检查该结果等等。 此类脚本通常记录在电子表格中,并且随着功能通过新开发或缺陷修复进行更新而需要维护。 这些脚本的次要目的是记录执行的实际测试。

通常,对于某些应用程序来说,脚本式手动测试过于僵化,或者测试过程和测试人员采用不太正式的方法。 脚本可能不是记录每个输入,而是编写为通用场景,在测试人员运行测试时为他们提供一定的灵活性。 在微软,手动测试 Xbox 游戏的人员经常这样做。 因此,输入将是“与法师互动”,而无需明确指定他们必须执行的互动类型。 因此,脚本式测试可以根据需要变得刚性或灵活,但为了使灵活性发挥作用,测试人员将需要关于如何处理选择和不确定性的非常具体的建议,而这更属于探索性测试的范畴。

在本书中,我们只对灵活类型的脚本式测试感兴趣。

探索性测试

当完全删除脚本(或者正如我们将在后面的章节中看到的,它们的刚性有所放松)时,该过程称为探索性测试。 测试人员可以以他们想要的任何方式与应用程序交互,并使用应用程序提供的信息来做出反应、改变方向,并通常不受约束地探索应用程序的功能。 对于某些人来说,它可能看起来是临时的,但在熟练且经验丰富的探索性测试人员手中,这种技术可能被证明是强大的。 倡导者认为,探索性测试允许充分发挥人脑的力量,在没有先入为主的限制的情况下发现缺陷和验证功能。

使用探索性方法的测试人员也不是没有文档跟踪。 测试结果、测试用例和测试文档是在执行测试时生成的,而不是预先在测试计划中记录的。 屏幕捕获和击键记录工具非常适合记录探索性测试的结果。 仅仅因为它是手动测试并不意味着我们不能使用自动化工具来辅助该过程。 事实上,即使是那些“手工制作”家具的人也会在电动工具的帮助下这样做。 手工制作测试用例也应该如此。 使用调试版本、调试器、代理和其他类型的分析工具的手动测试人员仍然在进行手动测试; 他们只是在务实地对待它。

探索性测试尤其适用于使用敏捷方法的现代 Web 应用程序开发。5 开发周期很短,几乎没有时间进行正式的脚本编写和维护。 功能通常发展迅速,因此最大限度地减少依赖性工件(如预先准备的测试用例)是一个理想的属性。 如果测试用例很有可能变得无关紧要,那么为什么还要首先编写它呢? 您是否不是在让自己花费更多的时间维护测试用例,而不是实际进行测试?

探索性测试的缺点是,测试人员可能会浪费大量时间在应用程序中四处游荡,寻找要测试的东西并试图查找缺陷。 缺乏准备、结构和指导可能会导致许多无效的工作时间,并一遍又一遍地重新测试相同的功能,尤其是在涉及多个测试人员或测试团队时。 没有文档,测试人员如何确保他们获得良好的覆盖率?

这就是指导发挥作用的地方。 没有良好指导的探索性测试就像在城市里四处游荡寻找酷炫的旅游景点。 有一个指南并了解一些关于您的目的地(在我们的例子中是软件)的东西,这有助于您的探索更系统化。 在伦敦寻找海滩是浪费时间。 在佛罗里达州寻找中世纪建筑也是如此。 当然,正在测试的内容与您如何测试它对您的策略同样重要。

探索性测试人员有两种类型的指导可以帮助他们进行决策:小规模探索性测试,它有助于在运行测试时进行本地决策; 以及大规模探索性测试,它帮助测试人员设计整体测试计划和策略。 两者都在此处进行了总结,并在第 3 章“小规模探索性测试”和第 4 章“大规模探索性测试”中进行了详细介绍。 最后,第 5 章“混合探索性测试技术”讨论了第三类探索性测试,它将探索元素与脚本式手动测试相结合。

小规模探索性测试

手动测试人员所做的很多事情都与变化有关。 测试人员必须选择应用哪些输入、访问哪些页面或屏幕、选择哪些菜单项以及在他们看到的每个输入字段中键入的确切值。 在我们运行的每个测试用例中,实际上都有数百个这样的决定要做。

探索性测试可以帮助测试人员做出这些决定。 当测试人员使用探索性测试策略来回答这类问题时,我称之为小规模探索性测试,因为决策的范围很小。 测试人员正在查看某个网页或对话框或方法,并且需要针对特定情况的专注建议。 这必然是一个本地化的决策过程,测试人员在单个测试用例中将执行数十次,在一天的测试过程中将执行数百次。

问题是许多测试人员不知道在他们遇到的各种“小”情况下该怎么做。 您在接受整数的文本框中输入什么值? 值 4 是否比值 400 更好(意味着更有可能找到缺陷或强制特定输出)? 0 或负数有什么特别之处吗? 您可以尝试哪些非法值? 如果您了解应用程序的某些信息——例如,它是用 C++ 编写的,或者它连接到数据库——这会改变您可能尝试的值吗? 实际上,我们可以使用哪些探索性测试智慧的总和来帮助我们在测试时做出正确的小决策?

第 3 章致力于传递其中的一些智慧。 我首先承认,其中大部分不是我的。 我很幸运能够与一些最优秀的软件测试人员一起工作。 从 IBM 到爱立信,再到微软、Adobe、谷歌、思科以及更多不太知名的公司,我收集了我认为大部分的建议,并在此处重现。 这些信息中的大部分都体现在如何破坏软件中,因此该书的读者可以将此视为对那里发布的知识体系的更新。 但是,由于那本书的态度是关于查找缺陷,因此本书的目的更为广泛。 在这里,我们感兴趣的不仅仅是查找缺陷。 我们希望迫使软件展示其功能,并获得应用程序的功能、界面和代码的覆盖率,并找到方法使其经受考验,以确定其发布准备情况。

大规模探索性测试

然而,测试不仅仅是正确地做出所有小的决定。 事实上,有可能完美地做出所有小的决定,但仍然没有一组能够确认(或拒绝)发布准备情况的整体测试。 所有测试用例的总和绝对大于各个部分。 测试用例是相互关联的,每个测试用例都应该添加到其他测试用例中,并以某种实质性的、可衡量的(或至少是可辩论的)方式使整组测试用例更好。

这表明需要一种指导测试用例设计和探索的策略。 单个测试用例应该访问哪些功能? 是否有某些功能或函数必须一起测试? 应该首先使用哪个功能,我们如何决定接下来测试哪些功能? 如果一个项目中有多个测试人员,我们如何确保他们的策略相互补充,并且他们最终不会测试相同的东西? 探索性测试人员如何就整体测试用例和测试策略做出这些更大范围的决策?

我称之为大规模探索性测试,因为要做的决策范围涵盖了整个软件,而不是单个屏幕或对话框。 决策应该指导如何探索应用程序,而不是如何测试特定功能。

在第 4 章中,我使用旅游隐喻来指导大规模探索性测试。 这样想:作为访问新城市的游客,您将使用大规模建议来选择要参观的餐厅,但您将使用小规模建议来选择要点的餐点和饮料。 大规模建议将帮助您计划一整天,并就如何计划您的整个住宿、您参观的地标、您观看的表演以及您用餐的餐厅提供建议。 小规模建议将帮助您驾驭这些事件中的每一个,并计划更大的计划总是会遗漏的细微细节。 通过完善两者的结合,您已经进入了专家级探索性软件测试人员的世界。

结合探索和脚本

没有必要将探索性测试视为脚本式手动测试的严格替代方案。 事实上,两者可以很好地共存。 拥有正式脚本可以为框架探索提供结构,而探索性方法可以为脚本添加变化元素,从而提高其有效性。 异性相吸的表达是相关的,因为正式脚本和探索性方法处于手动测试频谱的两个极端,它们实际上可以为彼此提供很多东西。 如果使用得当,每种方法都可以克服对方的弱点,并且测试人员最终可以处于非常有效的技术组合的快乐中间点。

我发现结合这两种技术的最佳方法是从正式脚本开始,并使用探索性技术向其中注入变化。 这样,单个脚本最终可能会被转换为任意数量的实际探索性测试用例。

传统的脚本式测试通常涉及用户故事或记录的端到端场景的起点,我们期望最终用户执行这些场景。 这些场景可能来自用户研究、应用程序先前版本的数据等等,并用作测试软件的脚本。 添加到传统场景测试中的探索性测试元素扩大了脚本的范围,以注入变化、调查和可选的用户路径。

使用场景作为指导的探索性测试人员通常会追求有趣的替代输入或追求脚本中未包含的某些潜在副作用。 然而,最终目标是完成场景,以便这些测试绕道总是最终回到脚本中记录的主要用户路径。 脚本的绕道可以选择基于修改脚本中特定步骤的结构化方式,或者通过偏离脚本的探索性偏移,然后再返回。 第 5 章专门讨论基于脚本的探索性测试,因为它是在手动测试人员的技术库中的关键工具之一。

第 3 章到第 5 章中的技术已在微软的许多案例研究和试验中应用,结果在第 6 章“探索性测试实践”中以参与这些项目的测试人员和测试主管编写的经验报告形式呈现。 第 6 章考察了探索性测试技术如何应用于从操作系统组件到移动应用程序再到更传统的桌面和 Web 软件等几个不同类别的软件。 此外,由作者描述了专门为特定项目编写的特殊游览。

本书的其余部分重点介绍了关于构建测试职业生涯和测试未来的文章,然后是我在佛罗里达理工学院担任教授和在微软担任架构师期间的过去和现在的文章和论文。 由于我现在已经离开微软,这本书可能是找到后者材料的唯一地方。

结论

手动探索性测试的世界是 IT 行业中最具挑战性和最令人满意的工作之一。 如果做得正确,探索性测试是一项战略挑战,也是手动测试人员与应用程序之间智力的较量,以发现隐藏的缺陷、可用性问题、安全问题等等。 长期以来,这种探索都是在没有良好指导的情况下完成的,并且一直是多年甚至几十年来学习其技能的专家的领域。 本书包含了许多经验和智慧,希望能够迅速涌现出许多新的专家,从而使更高质量的测试和更高质量的应用程序进入技术生态系统。

当测试软件时,我们需要人脑的存在。 本书中的信息旨在集中人脑,使测试尽可能彻底和完整。

练习

  1. 为什么我们不能只构建软件来测试软件? 为什么自动化不是软件测试问题的答案?

  2. 自动化擅长测试哪种类型的代码? 哪种类型的代码需要手动测试? 尝试形成一个理论来解释原因。

  3. 自动化擅长检测哪种类型的缺陷? 自动化不擅长检测哪种类型的缺陷? 举例说明。


脚注

  1. 我已故的导师 Harlan Mills 对此有一个有趣的看法:“程序中发生错误的唯一方式是作者将其放入其中。 没有其他已知的机制。 程序不会通过与其他有缺陷的程序一起坐着而获得缺陷。”
  2. 根据维基百科,术语“缺陷”的创造被错误地归因于最早和最著名的软件开发人员之一 Grace Hopper,她在对一只设法嵌入继电器的飞蛾做出反应时,称其为缺陷。 阅读 http://en.wikipedia.org/wiki/Software_bug 以了解该术语在硬件、机械甚至托马斯·爱迪生本人中的早期使用情况。
  3. 在我的脑海中,我设想了最终的开发人员缺陷查找工具,可以在他们键入代码时处理他们的代码。 它的工作方式类似于文档的拼写检查器。 开发人员在代码编辑器中键入缺陷的那一刻,错误的代码片段将被下划线,或者可能会自动更正。 关键是我们将缺陷的检测尽可能地靠近缺陷的创建,以便缺陷根本不会进入软件。 缺陷存在的时间越短,我们所有人都会越好。 但在 ऐसी 技术完善之前,我们将不得不继续测试。 我们将长期坚持下去!
  4. 缓冲区溢出是通过向输入字段注入比底层代码能够处理的更多数据来发现的。它们的原因和具体的查找方法在《How to Break Software Security》(Addison-Wesley,2004)第41页中有解释。
  5. 探索性测试的支持者人数现在已经足够多了,以至于不再需要为其辩护,尤其是在敏捷开发社区中。但是,我仍然在这里辩论它,以帮助那些仍然需要说服他们管理层的测试人员。


© 版权所有 Pearson Education。保留所有权利。

加载 Disqus 评论