1 探索性测试
本章将介绍探索性测试:手动试用新功能,快速获得有关其行为的反馈。我们将详细介绍探索性测试,考虑它的优缺点,以及何时应在项目中执行探索性测试。
我们将了解开始探索性测试所需的先决条件以及应采取的方法。这种测试可以是完整测试计划的一个缩影,它从客户的角度出发,利用你对功能工作原理的天真来识别容易混淆的地方。
探索性测试应作为大型测试策略的一部分,但在时间紧迫时也可单独进行。最后,我们将讨论在进行这种测试时应检查哪些内容,以及好奇心的重要性,无论是在这里还是在整个测试过程中。
在本章中,我们将讨论以下主题:
- 什么是探索性测试?
- 优点、缺点和替代方法
- 了解何时开始测试
- 了解测试活动
- 改进测试的螺旋模型
- 执行首次测试
- 规划新功能
- 在测试时利用你的天真
- 进行完整的探索性测试
- 根据需要使用探索性测试
- 检查探索性测试结果
- 在测试中使用好奇心
1.1 什么是探索性测试?
探索性测试以一种临时的、非结构化的方式使用新功能,以快速发现问题并了解其工作原理。它通常在测试周期的早期使用,以实现三个主要目标:让您了解您正在测试的功能、发现您需要的任何工具或知识,以及发现可能会延误后期测试的阻塞性错误。
在探索性测试中,测试的设计、实施和解释是同时进行的。这也被称为临时测试,因其无序的形式而备受诟病。然而,如果与其他测试技术一起使用,探索性测试在项目中是一个很有价值的阶段。在大型项目中,可以在早期阶段使用它来帮助规划更全面的测试;如果时间紧迫,探索性测试可能是唯一有时间进行的测试。
在理想的周期中,团队会在开发之初就计划好功能及其测试。在这种情况下,探索性测试会增强已有的测试计划。而在其他开发团队中,测试人员会在项目后期参与,只有在第一个版本已经运行后才会认真计划测试。在这两种情况下,探索性测试都是一个必要的步骤,它能让你看到新功能的实际运行情况,这样你就能根据确切的行为编写详细的测试计划。
探索性测试必须手动进行,这样您才能亲手操作新功能,了解有哪些输入和输出。其他部分的测试可以手动或自动化进行,但探索性测试必须手动进行,因为其主要目的不是查找错误,而是了解功能。基于这种理解,您可以计划进一步的测试。
不是每个人都能进行探索性测试。特别是,它需要代码开发人员以外的其他人员的参与。开发人员应确保该功能对他们有效,但探索性测试能显示该功能是否能在其他环境下对其他工程师有效。这就是探索性测试的第二个目标:发现足以阻碍进一步测试的重大问题。
如果新功能无法在测试环境中运行,如果页面出错,或者部分功能不可用,都会阻碍对这些整体区域的测试。探索性测试可以快速发现这些问题,以便在开发周期的早期进行修复,避免造成后期的延误。
首先,我们将考虑探索性测试的优缺点,以及发挥类似作用的其他方法。
1.2 优点、缺点和替代方法
探索性测试有明显的优缺点。它是测试周期的重要组成部分,但只有与其他测试形式相结合,才能减轻其缺点。在本书中,我们将看到不同测试形式的优势是如何互补的,以及为什么需要混合使用不同的方法才能获得对某一功能的全面覆盖。下面是探索性测试的一些优缺点:
探索性测试简单快捷。只要有运行代码,就不需要其他先决条件,如文档或测试工具。也就是说,应该有用户故事或其他指导材料,您可以根据这些材料启用功能并提高测试效率。查找错误很简单,因为您在使用产品的过程中就在观察它;您不需要检查监控控制台或自动测试输出的错误。您可以看到系统当时在做什么,因为您自己也在使用它。
缺点是很难重现问题。自动测试会精确执行一系列记录在案的操作,而自动测试则不同,如果你只是随便点击一下,而奇怪的事情却发生了,那么你做了什么来触发问题可能并不明显。有一次,我发现了一个错误,因为我在测试中途重新排列了屏幕上的窗口--改变浏览器窗口的大小导致了问题。我花了一些时间才意识到是这个原因导致了问题,而不是我在应用程序中进行的所有操作。
为了更容易找到错误的原因,您可以录制会话,可以在应用程序中录制,也可以在测试时使用的网络浏览器中录制。这需要一点时间来设置和审查,但对重现问题很有帮助。
虽然探索性测试不需要太多的先决条件,但对功能进行描述会有所帮助,这样你就能知道界面上不明显的功能区域。
另一个要求是探索性测试应由经验丰富的测试人员进行。由于探索性测试不需要审查或计划,因此其成功与否取决于执行者的技能。有经验的测试人员知道哪些地方可能导致错误,并能尝试过去失败过的测试。他们还知道要检查什么才能发现问题。初级测试人员无法达到同样的覆盖水平。
探索性测试所需的技能--好奇心和警觉性--在整个测试过程中都是必需的,即使在执行更正式的测试计划时也是如此。无论您是否正式执行探索性测试,您都应始终以同样的心态对待手动测试。
探索性测试的覆盖率也很难衡量。你用探索性测试测试了多少功能?实际上,作为更严格测试计划的一部分,您将需要再次执行所有这些测试,因此您将重复探索性测试期间所做的任何事情。因此,您应该限制在这些测试上花费的时间。它们能提供有价值的反馈,但前提是您必须从中学习。全面测试在流程的后期才开始。
为了衡量已达到的覆盖率,您可以与产品负责人和开发人员进行汇报,讨论已完成的测试。他们还可以建议尝试其他案例。请参阅第 3 章 "如何进行成功的规格审查",了解有关审查规格和应测试的情况的更多信息。
探索性测试的最后一个弱点是它不包括非功能测试。在这里,你只是检查功能是否能正常工作,而不是检查它是否能实现高负载、在多种环境下工作或从故障条件下恢复。所有这些测试都将在以后进行,但在现阶段并不是优先事项。
探索性测试的替代方法包括详细的规格说明、准备用户故事和用户界面模型,但在实践中,探索性测试是所有这些任务的补充。一份足够详细的规格说明应能提供足够的细节,让你可以直接据此编写测试计划。然而,在实践中,当你可以使用代码进行探索性测试时,完成规格说明的细节会容易得多。用户故事也是如此。它们对定义和完善核心功能非常有用,但通常不包括错误案例。这些错误案例在实际使用中也更容易发现。用户界面模型对于定义功能的外观和选项也有很大帮助,但在可用时进行实际试用仍然很有价值。
从探索性测试开始讨论测试似乎有些奇怪,因为探索性测试只有在功能的初始版本完成后才能开始。下文将介绍探索性测试在开发生命周期中的地位,并说明虽然它可能不是你开始的第一项测试任务,但却是你能完成的第一项任务。
1.3 了解何时开始测试
开发与测试之间没有明确的界限,即使测试是公司内部的一项专门职责。如下图所示,首先设计产品,然后构建产品,最后测试产品,这似乎是显而易见的:
这就是所谓的瀑布式开发模式。它的优点是有一个明确的测试起始点,即开发团队移交代码时。然而,虽然这种严格而简单的交接可能适用于桥梁和房屋,但软件开发却更为复杂--它必须处理许多不同的输入,而且有机会更加灵活。
在敏捷软件开发方法中,开发周期有多个相互作用的反馈回路,这些回路都有助于最终产品的开发。作为测试人员,您需要在这些周期的某个阶段开始工作。虽然测试可以等到开发人员对他们的产品完全满意之后再进行,但在实践中,这会增加太多的延迟。让测试人员尽早参与项目有很多好处:
- 帮助测试人员了解功能
- 让测试人员有时间准备所需的工具或数据集
- 让测试人员对设计过程提出反馈意见
- 让测试人员提前(比通常情况下)开始测试,即使只是有限的测试
这是 "左移 "测试的一部分,目的是让测试人员尽早参与项目,这是一个值得追求的目标,但也很有挑战性,因为没有明确的起点。没有哪一天任务完成了,测试就应该开始。相反,测试人员需要根据现有的规格和代码逐步开始测试。
早期测试的另一个挑战是与其他验证方法的整合。开发人员将(希望)对他们的代码进行手动检查,并为单个功能编写单元测试。如下图所示,这将产生错误和建议,并反馈到代码实现和功能规范中:
在敏捷开发模式中,规范仍将指导实施工作,并为开发人员和测试人员提供测试建议。但现在,可以有更多的反馈--规范可以根据技术限制和可能性而改变,然后开发人员会修复他们的错误,一些开发人员的测试可能会发现规范中需要修改的地方。
瀑布模型有一个特定的时间流程,从左到右--而在敏捷模型中,不同的任务之间是不断往返的。它们几乎是同时进行的。规范仍然是必要的第一步,用于说明开发人员应该实现什么,但在此之后,各个阶段在很大程度上是重叠的。例如,在测试驱动开发(TDD)中,测试将在实施之前进行。
请注意,本图中还不包括由独立测试功能执行的系统测试。这里描述的唯一测试是由开发人员自己进行的,这是测试组合中的一个重要组成部分。本书中描述的系统测试是对开发人员开始的测试的延伸和补充。
最重要的是,在敏捷项目中,这一流程不会一次性完成整个功能的测试。相反,功能会被分解成多个部分,每个部分可能由不同的团队成员并行开发:
与所示的瀑布模型不同,敏捷模型建议将任务拆分成尽可能小的功能单元,以便每个单元的工作都能并行进行,而不是完全指定和完全实现一个功能。在这个模型中,项目的不同部分在实施和开发人员开始测试时,所有这些交互都会同时进行。这种并行工作方式以及多层次反馈的机会,为敏捷项目提供了极大的灵活性。
当然,情况比这还要复杂,因为在实施功能的第 2 部分时学到的经验会反馈到第 1 部分,无论是从规范、实施还是初始测试,如下图所示:
在编写第 2 部分的规范时,您可能会发现需要对第 1 部分的规范进行修改。同样,第 2 部分的实施或开发人员测试也可能需要修改规范。第 1 部分的实施和开发人员测试也可能会受益于实施第 2 部分时学到的见解,这样这些开发任务之间就会有持续的反馈。
在这些混乱的交互中,系统测试应该从哪里开始?它在整个开发流程中的位置又是怎样的?在任何时候,功能的任何部分都会被部分指定、部分实现、部分被开发测试覆盖。
与瀑布模型不同,系统测试的起点并不明确。不过,系统测试在这个开发周期中还是有一个合适的位置,如果我们简化图表,只考虑一个功能,就会发现这一点。如下图所示,系统测试应建立在开发人员测试的基础上,同时为所有先前阶段提供指导和反馈:
考虑到单一功能,系统测试设计可以在规格说明开始后立即开始。一旦有了需求,就可以开发测试来检查这些需求。一旦开始实施,就可以使用白盒技术设计测试(参见第 6 章,白盒功能测试),以确保检查了每一条代码路径。系统测试应扩展和补充开发人员执行的测试。
系统测试还应为开发周期中的其他任务提供反馈。最明显的是,在系统测试中发现的错误需要在实施过程中加以修复。有些错误会导致规范的修改,特别是对最初没有描述的复杂情况的澄清或描述。系统测试还与开发人员测试相互影响。系统测试应避免与单元测试重叠,但也应覆盖单元测试中的空白,在系统测试层面确定的某些测试可能最好作为单元测试来实施,因此应将其转移到单元测试层面。
请记住,上图并没有按时间顺序显示任务。系统测试出现在右侧并不意味着它是最后执行的。与实现和开发人员测试一样,只要功能规范的第一部分准备就绪,就可以开始系统测试。你可以在开发人员开始工作之前,根据这些规范设计测试,并对规范提供反馈。
最后,我们可以将所有这一切结合起来,展示功能的多个部分同时开发和测试时的交互情况:
如上图所示,即使在如此高的细节水平上,也有很多事情需要管理。本书将介绍如何编写优秀的功能规范(参见第 2 章 "编写优秀的功能规范 "和第 3 章 "如何进行成功的规范评审"),以及如何在开发人员的实现和测试基础上进行构建(第 6 章 "白盒功能测试")。此外,你还需要考虑系统测试本身的细节,这将在本书的其余部分进行介绍。
接下来,我们将讨论系统测试所涉及的主要任务以及从何处着手。
1.4 了解测试活动
在进行系统测试时,有四项主要活动。如下图所示,每项活动都与其他活动相互影响、相互促进:
测试设计包括开始测试前的所有信息收集和准备活动,但不包括探索性测试,探索性测试的重要性足以自成一类。测试设计是指查看该功能的所有可用信息,以计划测试。测试所依据的书面材料统称为测试基础,可能包括以下文档:
- 用户故事
- 规格说明书(Specifications)
- 用户界面设计(UI)
- 技术设计文档
- 竞品分析
这些文档有助于展示新的行为,但您需要自己添加额外的细节。这将在第 2 章 "编写优秀的功能规范 "中进一步介绍。即使是书面信息也是不够的,还需要探索性测试带来的功能实际体验来补充。
从技术上讲,探索性测试是测试设计的一部分,因为其主要重点是收集信息,为将来的测试提供依据。然而,它发生的时间相对较晚,当有代码准备好进行测试时才出现,而且与测试设计的其他部分不同,它涉及测试并可能发现错误。由于测试设计具有双重作用,因此它有自己的类别。
有了测试基础和探索性测试的信息,您就可以记录功能规范和基于此的测试计划。这应详尽描述功能的行为,涵盖所有可能发生的情况。
详细测试将有条不紊地执行整个测试计划。这可能涉及手动测试或编写和运行自动测试。本书不描述如何运行测试;本书的重点是测试设计,说明应该运行哪些测试。
最后一步是进一步记录测试结果。这包括您需要提出的所有错误,以及对已通过测试的描述。
这些是测试人员的主要工作。围绕计划还有其他重要工作,包括分配人员和资源、估算时间尺度和安排工作。这些工作没有显示在上图中,也不在本书的讨论范围内,因为它们主要需要项目管理方面的技能。要成功运行一个测试项目,也必须具备这些技能,但在这里,我将重点放在需要进行哪些测试的细节上,以便你能尽可能准确地规划这些活动。
这四项测试活动中的每一项都会反馈到其他活动中。在记录和运行测试之前,设计测试是必要的,但测试结果也会显示哪些地方需要更多测试。一些测试计划对于探索性测试是必不可少的,但探索性测试也会显示哪些测试需要设计和执行以实现完整的覆盖。在探索性测试之前,就应开始记录功能规范,这样您就能知道会有哪些变化。然而,只有在探索性测试完成后才能完成文档编写,以回答出现的任何问题。
在这些相互关联的活动中,我们应该从哪里开始呢?从本章的标题就可以看出,我认为探索性测试是一个很好的开始。在此之前还会有规范和规划,但探索性测试是你可以完成的第一项测试任务。有了探索性测试,你就可以着手完成规格说明、测试计划设计和详细测试本身。由于这种独特的属性,我们将从它开始。
对于功能的每个部分,测试都应在这些不同的测试活动之间进行,并反馈到前面所述的编写规范、实施和开发人员测试等其他设计任务中。这些任务是如何结合在一起的,又是如何朝着发布规范完善且经过测试的功能这一目标前进的呢?下文将介绍有序的活动流程及其进展情况。
1.5 测试改进的螺旋模型
从最初的规范到详细、完整的测试计划,测试的开发过程可以看作是四个重复阶段的螺旋循环。当然,在实践中会更加复杂,不同阶段之间会有大量的来回切换。这种简化说明了生成测试计划所需的主要里程碑以及它们之间的主要流程。它类似于 Barry Boehm 的软件开发螺旋模型。不过,这个模型只考虑测试计划的开发,而不是整个软件开发周期,而且不是向外螺旋式上升,而是向内螺旋式上升,以达到测试的完美:
迭代测试计划需要经历以下四个阶段:
- 准备规格和计划
- 讨论和审查
- 执行测试
- 分析和反馈结果
软件开发始于产品负责人的初始规范,这是一个重要的开端,但需要多次迭代才能完成。然后,产品负责人介绍并讨论该功能。根据最初的规格,开发团队可以准备初步的实施,你也可以为探索性测试提出想法。
一旦初步实施完成,就可以开始改进规范、测试计划和代码本身。首先是探索性测试,也就是上图中的第 3 步。通过实际试用代码,你可以更好地理解代码,并准备本章所述的进一步测试。虽然之前有几个必要步骤,但改进代码的过程始于探索性测试。
有了步骤 4 中的探索性测试结果,您就可以编写功能规范了,如上图步骤 5 所示。这将在第 2 章 "编写优秀的功能规范 "中详细介绍。然后,需要对该规范进行审核--通过正式讨论来逐步完善其细节。审核是第 6 步,将在第 3 章 "如何进行成功的规范审核 "中介绍。
审核完成后,就可以对功能进行详细的测试。这一个小方框--上图中的第 7 步--是本书大部分内容的主题,将在第 4 章至第 13 章中介绍。
然而,编写测试计划并不是终点。根据详细的测试结果,你可以完善规范,对其进行讨论,并进一步执行有针对性的测试。例如,这可能是为了验证你提出的错误,也可能是为了扩展测试计划中的错误群。测试结果应为今后的测试任务提供参考。这些反馈会改进本周期和后续周期的测试工作,逐渐趋向于测试的完美,但永远不会达到完美。
在这个螺旋式上升的过程中,代码的文档和质量也会随着功能的检查和错误的修复而不断改进。
上图显示了规范中对功能的理论描述和测试基础的其他部分如何与代码本身的实际测试结果相结合,以提供全面的测试覆盖率。只依赖文档意味着你会错过对代码问题做出反应的机会。没有文档的测试依赖于你对代码应该做什么的假设,而不是代码的预期行为。
通过这种循环,您可以彻底了解您正在开发的功能,并对其进行高质量的测试。虽然这只是循环中的一个点,但我们还是要从描述探索性测试开始,从第一个重要问题开始:这个功能是否已经准备好进行测试?
1.5.1 确定功能是否可以测试
测试一个尚未准备好的功能很容易浪费时间。如果开发人员已经知道他们还没有实现该功能,就没有必要再提出错误。另一方面,测试应尽早开始,以便在开发人员对代码还记忆犹新时快速发现问题。
协调这些相互冲突的目标的方法就是沟通。测试应尽早开始,但开发人员应清楚哪些是可测试的,哪些是尚不能工作的。如果你是根据一份详细的、有编号的规范(参见第 2 章,编写优秀的功能规范)来工作,那么他们就可以明确说明哪种构建满足了哪些要求。可能连开发人员自己都不知道某个功能是否能正常工作,例如,如果他们认为某个新功能可以正常工作,但却没有亲自尝试过。只要开发人员明确表示他们不确定该行为,以便您可以进行尝试,就没有必要花费大量时间来收集这些信息。
此外,还要注意测试那些快速变化的代码或需要进行大量架构修改的代码。如果你今天测试的代码明天就会被重写,那你就浪费了时间。虽然尽快开始测试是件好事,但这并不意味着一有工作代码就开始测试。这些代码必须是稳定的,并且是拟议发布版本的一部分。开发人员编写的单元测试可以说明代码已经足够稳定,值得进行测试;但如果代码还没有准备好,那就找点别的事情做吧。
现实世界的例子--神奇的消失界面
我曾经是一个新硬件项目的测试团队成员,该项目将进行视频会议。该项目将有两种产品:一种负责媒体处理,另一种负责呼叫和用户界面,两者之间有详细的应用程序接口。测试团队非常有组织,在开发周期的早期就开始了测试,对两个产品之间的 API 实施了一整套测试。
随后,架构发生了变化。为了简单起见,两个产品将合并在一起,我们将始终把它们放在一起销售。应用程序接口不会向客户公开,而是进行了实质性的改动,使其在内部运行。我们所有的测试都是在浪费时间。
不应过早开始测试,这听起来很明显。但是,在项目中期,你很难注意到产品还没有准备好--开发人员忙于编码,而你正在测试并发现了严重的错误。但是,如果在同一区域反复测试、架构发生重大变化,以及对某项功能的哪些部分已经实现、哪些仍在开发中感到困惑时,就要注意了。这说明你需要更好地与开发团队沟通,了解他们已经完成了哪些领域的代码,哪些是真正准备好进行测试的。
当开发团队确定了架构并完成了初步实施后,你就应该开始测试了。不过,让新功能首次投入运行是一项挑战,因此下一节将介绍如何使这一过程尽可能顺利。
1.6 进行首次测试
项目的一个重要里程碑就是能够进行测试。功能首次运行意味着所有代码都已就位,并能在测试环境中运行。代码已成功从开发系统转移到其他地方运行,无论是在完整的测试环境、测试线束、本地机器还是容器化环境中(有关测试系统的讨论,请参阅第 5 章 "黑盒功能测试")。运行功能会带来大量可能的测试。
例如,如果您正在测试网站的注册页面,该页面是否加载并接受输入?如果是,那么就可以执行许多后续测试类型。如果没有,那就告诉开发人员你还不能开始测试。
进行第一次测试需要完成许多开发任务。另一个浪费时间的简单方法是测试尚未启用的功能。您也可以通过加强与开发团队的沟通来解决这个问题。
规范会说明功能应该做什么(参见第 2 章,编写优秀的功能规范),但测试人员还需要另一层细节,即功能是如何配置的。测试开始前,确保了解以下内容:
- 系统所有相关部分的最低版本要求是什么
- 必要的配置是什么
- 如何检查功能是否正常
版本要求已经很清楚了--在测试之前,你必须运行带有新代码的版本。但有时,系统中哪些部分相互依赖并不明显。或者,虽然功能是在 5.3 分支中实现的,但它是在 5.3.8 还是 5.3.9 版本中呢?一项功能可能是零散交付的,在这种情况下,每个版本中到底包含哪些功能?
首次测试时,只尝试最基本的功能。新屏幕是否加载,API 调用是否被接受,或者新选项是否可用?在把时间花在第一个案例上之前,要清楚你需要哪些版本。该功能需要启用哪个功能标志?需要更新哪个设置,在哪个文件中?同样,挑战在于从开发团队那里获得所有必要的细节,以避免浪费时间去寻找这些细节。
如果您已经具备了所有要求,但发现了几个阻塞问题,请向开发团队确认该功能是否已准备好进行测试。开发人员应该已经在自己的系统上做了足够多的测试,可以确信该功能对其他人也有效,但事实并非总是如此。如果反复出现问题,请让开发人员重新检查他们的代码。
最后,如何判断功能是否有效?有时,功能很明显是客户可见的--新网页是否出现,或者新选项是否出现?但有时,很难判断新功能是否启用,尤其是在代码重构或微妙的性能变化期间。你应该注意哪些日志行?哪些统计数据会表明这一变更正在被使用?
这些细节不会出现在功能规范中;同样,这也是测试团队需要的额外细节,以检查特定代码构建中的行为甚至功能是否存在。
从项目的角度来看,运行第一个测试是关键路径。开始测试会延误其他所有工作,因此要确保尽早完成测试。不要花很多时间准备好一切,例如,完成其他六个项目,这样一个庞大的测试团队就可以对某一功能进行测试了......却发现该功能无法运行,根本无法进行测试。尽早检查功能,确保功能基本到位,如果开发人员需要做任何修改,就迅速将其反馈给他们。一旦第一次测试通过,就可以暂时搁置一段时间,直到可以正常测试为止。但首先要确保准备就绪。
一旦功能运行正常,真正的工作就开始了。探索性测试从哪里开始将在下一节中介绍。
参考资料
- 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
- 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
- python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
- Linux精品书籍下载 https://www.cnblogs.com/testing-/p/17438558.html
- 如需英文原版可联系微信pythontesting
1.7 规划新功能
要开始探索性测试,您需要做到三点:确信代码足够稳定,可以进行测试;运行所需的版本;配置正确。准备就绪后,探索性测试就可以开始了。
重要的是要牢记探索性测试的目的。这不是详细的测试,其结果将在未来依赖。探索性测试期间所做的一切都有可能在以后更正式的一轮测试中重复,因此探索性测试的时间应该有限,不宜过长。在这一轮测试和以后的测试中,重复劳动很容易浪费时间。只有在达到探索性测试的三个目标后,才能进行测试:
- 了解功能,计划进一步测试。
- 确定执行进一步测试所需的工具和知识。
- 发现阻碍进一步测试的错误。
首先,也是最重要的一点,您可以通过了解功能来准备功能规范。在理想的情况下,你会提前准备好功能规范,而实现过程也会与之完全一致。然而,在开发过程中,经常会出现一些变更;例如,某些功能可能会推迟到以后的版本中。有时,后来的讨论和修改并没有写入规范中,尤其是关于用户界面的讨论和修改。产品规格通常不会详细说明错误案例、安全性和加载行为。探索性测试让你有机会了解这次发布完成了哪些变更,哪些没有,功能的哪些部分被放弃了,哪些被添加了。
好的探索性测试会检查每一个屏幕,阅读每一个字段,在每一个输入中输入细节,并至少按一次每一个按钮。不要试图覆盖所有可能的输入值或对功能进行详尽的测试;只需尽可能多地查看。以下是你应该覆盖的一些领域:
- 加载每个页面/屏幕
- 在每个输入中输入详细信息
- 使用每个功能
- 每种状态的转换(如拨号、振铃、来电、挂断,或注册、等待验证、已验证、已登录等)
- 检查用户可见的输出
- 检查内部状态(通过日志、数据库内容等)
通过试用功能,您可以了解其实际运行情况,并将规范付诸实践。对于开发人员和产品所有者来说,如果眼前就有一个正在运行的功能,那么发现问题和考虑后果就会容易得多,因为他们只能想象它的样子。充分利用这一优势。
功能的某些方面可能无法使用,例如,如果它是一个应用程序接口,你需要实现一个客户端来驱动它,或者如果你需要在使用前生成特定的数据集。这就是探索性测试的第二个目的:发现在实践中进行详细测试所缺少的东西。同样,希望这一点在最初的功能说明和测试计划中已经明确。然而,探索性测试是对这些计划是否能真正使用的重要检查,并能发现需要进行的任何修改。
测试结束时,您应该知道与该功能相关的所有配置选项及其效果。这对绘制全面功能测试的因变量和自变量图至关重要。例如,跟踪用户年龄可能只是为了获取信息,而不会改变该功能或产品的任何行为。它被写入数据库,读取后只是简单地显示给用户。在这种情况下,它与功能的其他方面无关。也有可能某些选项或行为只出现在特定年龄的用户身上。那么,您就需要检查每个年龄段和这些功能的组合,以确保每种情况下的行为都是正确的。这就是你看到这些交互的机会。
探索性测试的第三个目的是找到阻碍进一步测试的主要问题。就像完成第一项测试是发布功能的关键路径一样,任何严重到阻碍测试计划整个部分的错误也是如此。例如,如果一个页面根本无法加载,你就无法测试它的任何输入或输出,或者如果一个应用程序在使用初期崩溃,你就无法尝试之后的场景。探索性测试是您快速检查一切是否可测试并准备就绪的机会。在准备全面测试时,你可以向开发团队提出问题,而不是在准备就绪后再拖延项目。
探索性测试结束时,目的是能够在以后进行更彻底的测试。你应该对界面、该版本实现的所有输入和输出、配置选项和基本功能有详细的了解。缺少的是细节和交互,我们将在第 5 章 "黑盒功能测试 "中讨论。
探索性测试是一种快速、有趣的了解新功能的方法,不需要像稍后设计的完整测试计划那样严格。早期阶段的挑战在于你对产品知之甚少,但正如下一节所述,你可以利用这一优势。
1.8 在测试时利用你的天真
探索性测试是反馈功能可用性的理想机会。当你开始测试时,你可能是第一个看到这项功能的人,但却没有参与设计。首先,缺乏经验是一种优势。作为测试人员,你的任务是注意和探索各种可能性,避免因经验丰富而产生的偏见和期望。
其次,了解代码的设计和实现也很重要。第 6 章 "白盒功能测试 "中介绍的白盒测试技术要求您检查所有代码路径,并利用系统知识尝试每种特殊情况。然而,在一开始,这种知识的缺乏对于发现令人惊讶或意想不到的结果非常重要,尤其是对于面向用户的界面和功能。任何让你感到意外的结果也可能会让你的客户感到意外,因此要留意任何不明显的结果。
记录下你在查找过程中遇到的困难、你不得不读两遍的文字以及你在使用该功能时感到意外的事情。这些都是对用户体验设计至关重要的反馈。如果你在第一次使用时没有理解某些内容,不要认为这是你的错--这可能是设计者的错,因为他没有把它说得更清楚。有些主题本身就很复杂,在用户理解之前需要有一定的背景知识;然而,你的产品的任何用户都可能对其现有功能或该领域的其他产品有一定的背景知识。设计良好的界面应该能够直观地利用这些知识。如果不是这样,那也是一个缺陷。有关可用性测试的更多信息,请参见第 8 章 "用户体验测试"。
用户体验的世界没有确定的答案,对你来说不明显的东西并不意味着对其他人也是如此。与测试的其他部分不同,产品是否符合规范应该有一个明确的答案,而用户体验则主观得多。值得提出任何你认为具有挑战性的问题,以收集其他人的意见。如果有足够多的人认为某些地方令人困惑,那就有理由对其进行修改。你必须强调这些问题,以便开始讨论并决定如何改进。
抱着这种天真的态度,对各种可能性持开放态度,并对每一种可能性进行研究,您的目标应该是触及新功能的所有主要功能。在此基础上,您可以使用所有可用的不同测试类型,完成新功能测试的微缩版。本书后续章节将对这些测试进行更详细的介绍,但这是你在项目早期快速执行不同类型测试的机会,我们将在下一节中学习。
1.9 运行完整的探索性测试
探索性测试是以下所有测试的缩小版,从每个部分中抽出几个最重要的测试。它是入门测试,这意味着它简要地涵盖了许多领域,就像本章介绍本书一样。探索性测试应涵盖以下方面:
- 黑盒功能测试
- 白盒功能测试
- 错误案例
- 用户体验测试
- 安全测试
- 可维护性测试
- 非功能测试
与完整的测试计划相比,探索性测试的时间较少,因此应谨慎确定这些方面的优先顺序。
正如我们已经看到的,探索性测试从黑盒功能测试开始,不使用底层实现知识,只专注于工作案例。这应该是你所做的大部分工作。
即使在探索性测试期间,天真地了解功能是如何实现的也有好处,但了解其架构和设计的一些细节还是很有帮助的。如果该功能的一个次要方面需要大量代码,那么您应该对该方面进行远比它的用途可能暗示的更多的测试。如果实现该功能需要更多代码,那么出错的可能性就会更大,出现缺陷的几率也会更高。因此,即使是在探索过程中,也有必要在了解功能设计的基础上进行一些白盒测试。白盒测试应在黑盒测试之后进行,这样您就可以充分利用您对其行为缺乏假设的优势。
您还可以在探索性测试中开始尝试错误案例,例如,故意留空字段、输入无效信息或尝试触发错误。这不应该是重点--确保功能正常运行才是第一位的。但即使是在早期,您也可以使用错误输入探查行为。
如前所述,探索性测试是发现可用性问题的绝佳时机,因为在你了解一项新功能的内外部工作原理之前,它对你来说还是一项新功能。对可用性的反馈应该是所有探索性测试的关键成果。
您还可以在探索阶段测试安全性。同样,你需要确定这些测试的优先级--最明显的攻击可能是快速运行 SQL 注入和脚本攻击,或者试图在没有必要权限的情况下访问信息。所需证书是否到位,流量是否加密?有关如何运行此类测试的详细信息,请参见第 9 章 "安全测试"。诸如此类的重大缺陷很容易被发现并及早提出。与功能和可用性测试相比,安全性不应该是探索性测试的重点,但可以从这里开始。
探索性测试还可以开始检查代码的可维护性。日志有多大用处?是否记录了应用程序的操作?是否生成了相关事件?对这项服务有哪些监控措施?在项目早期,在第一版代码中,答案可能很简单,只需指出事件尚未准备就绪,或其中存在空白。这就是开始编写这些需求列表的时候了。可维护性在项目的优先级列表中可能很低,因此尽早注意到这些需求很重要。
探索性测试通常不包括非功能测试,因为非功能测试通常需要脚本和工具来练习,这比可用时间要长。不过,如果你已经准备好了以前测试的工具,而且该功能非常相似,你可以快速修改它们,那么你就可以进行初步的性能和可靠性测试(详见第 12 章,负载测试)。同样,与可用性和功能测试相比,这并不是优先事项。
在进行这些类型的测试时,记下在完整测试计划中应涵盖的想法和领域。探索性测试是尝试一些想法的时候,同时也是确定以后要花更多时间在哪些方面的时候。您不可能在一开始就涵盖所有内容,因此请记录下您遗漏的内容,以便日后再做。
这也是您发现缺陷趋势的机会。缺陷不是随意散布在产品中的,它们是成组的。因此,请检查该功能或产品的哪些部分尤其容易出现问题。调整你的测试计划,在这些地方增加额外的细节,以发现更多的问题。这样,你的测试就能对功能做出反应,从而最大限度地发现错误。有关反应性测试的更多信息,请参阅第 5 章 "黑盒功能测试"。
测试结束后,你应该对该功能的工作原理有了很好的了解,并确保它不会出现任何重大问题。通常情况下,这将为后续更详细的测试做好准备,但有时,正如下一节所解释的那样,这也是仅有的可能。
1.10 必要时使用探索性测试
虽然探索性测试通常用于大型功能测试的开始阶段,但当你没有时间做其他事情时,探索性测试也非常重要。正如我们所看到的,探索性测试将测试设计、执行和解释整合为一个步骤,以较少的计划和文档为代价实现了高效率。在经验丰富的测试人员手中,它能迅速提供广泛的覆盖范围,并迅速获得对变更的信心。
在项目接近尾声时,当有小的改动来修复关键错误时,这种方法就非常有用。您需要检查这些更改,但也必须执行回归测试,以确保没有引入其他错误。回归测试包括对现有功能进行测试,以确保这些功能仍然有效,没有被破坏。探索性测试主要关注新行为,但也可以检查产品的主要功能。
- 实际案例--修复 xml
在长达 6 个多月的瀑布式开发周期结束时,我们几乎准备好发布新版本的产品。我们发现了最后一个阻塞错误,需要重新构建,于是开发团队进行了修改,并向我们提供了候选发布代码。不幸的是,他们并没有只修复阻塞错误。在这一领域,开发人员还注意到我们的 XML 格式有误,并借此机会进行了修复。
开发人员是对的。格式是不正确的,但他没有意识到读取数据的系统依赖于这种不正确性。修复了格式问题后,其他系统就无法再读取信息了。这个关键的错误是在发布周期的最后引入的,但我们通过探索性测试很快就发现了它。这耽误了整个项目的进度,因为所有工作都必须停下来等待修复,但我们很快就回滚了更改。
在时间紧迫的情况下,探索性测试是对变更进行广泛覆盖的最快方法。您可以迅速获得功能正常运行的信心,但要注意,如果您只进行了这些测试,就不要过于自信。即使在探索性测试通过后,仍可能存在阻塞问题。探索性测试通常无法很好地覆盖非功能测试:功能是否能在所有网络浏览器和操作系统版本上运行?是否能在高负载下工作?是否能应对恶劣的网络条件?即使该功能在某个测试人员的环境中运行良好,实际情况也可能会出现问题。
因此,请注意探索性测试并不全面。与其在这种测试形式上投入更多精力,不如利用它来制定详尽的测试计划,并将其记录在案和实现自动化。即使你有更多时间,也应始终限制在这种测试形式上花费的时间。
1.11 检查探索性测试结果
虽然探索性测试的输入通常很明确--使用所有新功能、选择所有新选项--但检查其行为却可能很模糊。如果用户界面的某些部分尚未实施,可能就不会有明显的变化。唯一可用的输出可能是数据库字段或日志行,显示内部状态已发生变化。如果有用户可见的变化,也可能是偶然的--例如,界面的变化表明发生了另一个变化。
有时,在进行其他更改之前,不可能进行完整的测试。例如,系统的一部分已经准备就绪,但使用它的另一部分还没有准备好。在这种情况下,您可以检查新功能是否准备就绪,是否破坏了任何现有行为,但完整的系统测试必须等到所有元素都能一起使用时才能进行。如果是这样,您可以完成一些测试,并留下一个任务,在代码准备就绪时完成测试。
在项目早期,可能没有太多文档,或者规格说明可能不足以详细描述数据库实现和日志输出。无论如何,您都需要与开发人员进行交流,以确认您期望在此代码版本中看到的具体变更。只有掌握了这些信息,你才能确信你不仅使用了所有的变量,而且这些变量也达到了预期的效果。
除了开发团队建议的输出之外,你还要留意其他任何奇怪或不寻常的事情。在运行测试和检查时都要保持好奇心。好奇心在整个测试过程中都至关重要,尤其是在探索性测试中,这将在下一节中介绍。
1.12 在测试中使用好奇心
在整个探索性测试过程中,要采取的方法就是最大限度地激发好奇心。不断地问自己:"我想知道如果我这样做会发生什么?在测试过程中,您能想到的所有问题都很有价值;测试人员就是要提出正确的问题。
所有测试都需要同样的好奇心,但在探索性测试中,您可以最清楚地看到这一点,因为在探索性测试中没有测试计划,只有您的好奇心。
然而,所有的好奇心都是不一样的,探索性测试就是一个从经验中受益匪浅的领域。在有限的时间内,你必须直接瞄准系统的薄弱点,避免浪费时间去测试成熟的代码,而这些代码在之前的周期或自动测试中已经得到了很好的覆盖。
一个潜在的薄弱环节就是之前没有测试覆盖的新变化。但是,您应该将哪些现有功能与这些新功能结合起来才能发现问题呢?这就是您的经验发挥作用的地方。你的系统有哪些弱点?以前在哪里发现过错误?
国际软件测试资格委员会(ISTQB)是一个致力于改进软件测试实践的组织,为从业人员提供认证计划。他们指出,错误并不是均匀分布在整个代码中,而是倾向于聚集在某些区域。
影响许多系统的典型问题举例如下:
- 在内存或 CPU 有限的低规格机器上运行
- 在低分辨率屏幕或小窗口尺寸上运行
- 升级后过渡到使用新功能时的行为
- 边界和边缘条件问题
- 时间和时区问题
- 文本输入问题(空白、过长、Unicode 字符、SQL 注入等)
- 备份和还原
- 恶劣网络条件下的行为
除上述问题外,还要跟踪影响特定应用程序或网页的所有弱点。创建一份文档,列出过去发现的薄弱环节,并在发现新问题时及时更新。
正如 ISTQB 教学大纲所述,这种方法被称为 "错误猜测"。它强调测试人员需要凭借经验来预测错误可能发生的位置。通常,脱离测试计划就能发现最好的错误。这就是 ISTQB 测试原则关于除草剂悖论的必然结果--你用一种特定的方法测试得越多,发现的错误就越少,这就好比使用除草剂会逐渐杀死更少的杂草。只有那些受其影响较小的杂草才能茁壮成长。以前运行过很多次的测试,这次不太可能发现问题,此外,这些测试背后的想法可能已为开发和测试团队所熟知,因此开发人员会记得处理这种情况。你更有可能在新的测试想法和以前未曾考虑过的新功能组合中发现错误。
由于探索性测试依赖于经验,因此最好由资深测试人员进行。初级测试人员可以负责测试流程的其他部分,如实施他人设计的测试计划或回归测试。对于探索性测试,应确保由经验丰富的团队成员来领导。如果您是测试经理,您可以做出这样的决定;如果您是被委以重任的测试人员,请让其他人知道是否有更合适的人选。当然,经验可以针对特定领域。你可能在某一领域经验丰富,但也许其他人更适合对这一特定功能进行探索性测试。
在选择检查内容时,经验也很重要。虽然有些错误在用户界面上是可见的,但其他错误可能很隐蔽,如日志中的警告或数据库字段被错误写入。在决定要做什么时,除了要利用自己的好奇心外,还要确保在系统中搜索可以检查的问题和项目。
好奇心在整个测试过程中都至关重要,而不仅仅是在探索性测试期间。临时测试和按计划进行的测试之间没有硬性的界限。相反,这是一个范围。虽然探索性测试没有什么结构,但像前面提到的可能存在的弱点清单也有助于指导测试。因此,即使是探索性测试也可以有一些文档。
功能测试可以是描述性的,例如,"上传一张 .jpeg 图片 "这样的测试意味着每个测试人员都会选择不同的图片。这在扩大覆盖面的同时,也降低了测试的可重复性。或者,测试可以是规定性的,准确描述要做什么;例如,"上传图片 test1.jpeg"。即使在这些测试中,环境也会发生变化,系统的状态也可能不同,或者你可能会以不寻常的顺序运行任务。始终寻找新的测试方法和新的尝试。
测试是一场创造力的军备竞赛,测试人员会努力寻找新的方法来破坏产品,而开发人员则会在不经意间增加新的问题。这就是测试的乐趣和技巧所在,所以请尽情享受吧!测试新功能时,您是第一个做前人没有做过的事情的人。你是军队的先锋,冲锋陷阵;你是进入未知领域的先驱。因此,利用你的经验,准备好迎接惊喜,睁大眼睛迎接意外。
1.13 小结
本章介绍了探索性测试--何时进行,由谁进行。我们看到了探索性测试在开发周期中的位置,也看到了探索性测试是在代码刚开始实施不久就发现问题的有力工具,因为它速度快,几乎不需要计划。但是,探索性测试需要高级工程师才能完成,而且没有广泛的审核,也不会产生大量的文档。很难判断探索性测试的覆盖范围,非功能测试的覆盖范围可能很小。这样,即使探索性测试通过了,在实际使用中仍有可能出现问题。探索性测试的目的应该是更好地了解功能,以便日后准备全面的测试计划。
本章介绍了何时开始探索性测试,代码仍在变化时不要过早开始,以及成功运行第一个测试的步骤。在测试过程的开始阶段,我们看到了好奇心和天真的重要性,这对于选择测试内容和检查测试结果都很重要。最后,我们学习了如何绘制功能图,为执行完整测试流程的微缩版做好准备。
下一章将介绍探索性测试的经验,并将其应用于编写详细的功能规范,以指导所有后续测试。