草庐IT

不可替代的测试人:一文解释探索性测试是什么

程序员威子 2024-05-11 原文

剧本式测试

随着软件技术的发展,软件开发过程中对软件测试的需求越来越庞大,从原来单纯的寻找软件缺陷,到后来的学习软件、挖掘软件中存在的缺陷、评估软件可用性、性能等等方面,软件测试发挥着重要作用。

考虑到软件测试如此的重要,难免会有不少的测试人员试图完成“完全测试”——通俗地说即使想将软件的方方面面、每一行代码都测试覆盖到。

但是,“完全测试”是不存在的,是一个难以实现的梦想,究其原因,软件测试的投入(时间、人力等)是一个巨大的限制因素。我们不可能围绕一个软件投入无穷的时间和人力进行测试,并且软件是一个不断发展和迭代的产品,当测试人员在前一个迭代周期测试的一个功能可能在下一个迭代周期就会发生变化,测试的要点也会相应的发生变化。

众所周知,传统的脚本式测试流程的主要包括测试设计和测试执行。测试人员严格地按照设计好的测试用例执行测试,比对期望输出和实际测试输出,最终判定测试结果通过与否。

由此可见,脚本测试严重依赖于测试设计人员对被测功能的熟悉度,忽视了测试执行人员在测试过程中发挥的作用(某种程度上认为测试人员只需要机械性地严格按照测试脚本执行即可)。

但在实际工作中,剧本式测试往往存在以下问题:

1)测试设计人员不可能严格的、精确的描述每一个测试用例

在工业实践和真实的生产环境中,测试用例往往很难设计,实践人员也很少会严格按照设计的测试用例进行测试,预期结果的设计和评估都需要依靠测试人员要根据经验和隐性知识,而不是严格地记录技术和结果。

2)测试人员需要花费大量的时间维护测试脚本

软件应用具有开放性、快速迭代、频繁变更、存在多种接口、重用户体验等特点,相应的测试用例也需要频繁修改,因此需要耗费大量人力和时间成本维护测试用例或测试脚本。

3)剧本式测试忽略了测试人员在测试执行中的创造性

有数据表明,不同的测试人员使用相同的测试策略、按照相同的测试脚本执行测试,测试结果(挖掘出的缺陷数量和缺陷质量)也会存在较大差异。

4)剧本式测试的机械性特点导致测试人员的功能性被削弱,测试人员的可替代性被增强

正是由于剧本式测试的机械性,已有的许多关于剧本式测试的研究的关注点都在于自动化测试用例的设计、生成和优先级,或者测试技术的选择,而非测试人员本身,导致生产实践中不少企业或个人都认为软件测试是一项可以被机器替代的工作。

探索性测试

无数经验告诉我们,影响用例设计、用例执行过程的不只是单纯的测试技术,测试经验和智力性创造也发挥着重要作用。为了提高测试工作的效率,充分发挥测试人员在测试工作中的智力性,“探索性测试”在剧本式测试的包围圈中异军突起。

探索性测试最早是由测试专家 Cem Kaner 博士在 1983 年提出,并发展成为一门学科。它指的不是某种具体的测试技术,而是一种自由的测试风格或者说测试思维技术。

显著区别于其他测试方法的地方在,它不依赖于设计好的测试用例,强调测试设计和测试执行同时进行,其本质是边学习、边设计、边测试、边思考。

探索性方法的重点是基于前面测试的结果,动态识别和测试具有高风险的测试场景,而不是基于一些静态的覆盖率概念。

在某种意义上,探索性测试反映了测试方法的完全转变,其中测试执行是基于测试人员当前和改进的对系统的理解。这种理解来源于:测试期间观察到的产品行为,熟悉应用程序、平台、缺陷过程、可能出现的缺陷类型失败,与特定产品相关的风险,等等。

探索性测试从诞生至今,经过不断地发展已经取得了不小的实践成果。不少人认为:相较于剧本式测试,探索性测试可以让人脑不受各种先入为主的条条框框约束,能够更好地利用人的创造性、经验能力和技巧。

探索性测试效益

Q1:相较于剧本式测试,探索性测试能够发现更多数量、更多类型、更严重的缺陷吗?

软件中发现缺陷的数量主要取决于测试人员测试软件的技能。传统的脚本测试很少能体现出测试人员的重要性,更多的是检查需求的一致性。基于文献中的实验和真实案例分析:由于探索性测试的自由性,测试人员可以不必花费过多的时间在文档上(如学习需求文档、使用说明等),而将时间更多的用于被测产品的学习和探索。

Q2:相较于剧本式测试,探索性测试能够降低缺陷误报率、提高检测缺陷的效率吗?

相较于剧本式测试,探索性测试的缺陷误报率更低。快速反馈,缩短开发人员与测试人员之间的反馈时间。因为探索性测试人员可以对产品的变更做出快速反应并提供测试将结果反馈给开发人员。探索性测试人员可以基于系统实际行为的信息重点针对疑区域进行测试,而不是仅仅依赖于规范和设计计划测试时的文档。

有文献表明,使用探索性测试能够在较短的时间内完成测试,在单位时间内能够发现数量更多的缺陷,相较于剧本式测试更高效。

Q3:相较于剧本式测试,探索性测试能为测试人员带来什么好处?

同步学习。探索性测试可以帮助测试人员提高对测试的兴趣,当测试人员不遵循预先指定的脚本时,他们会积极地学习被测试的系统,并获得关于系统行为和失败的知识。

鼓励测试人员在完成测试任务时探索任何看起来有趣、可疑或有价值的东西,以获得技能或知识,这也可以激发测试人员保持持续性学习的动力。特别是对于新手来说,探索性学习是获取新系统重要知识的有效途径。探索性测试花在文档上的时间更少(即,只专注于日志、测试笔记和执行后的视频的文档),测试人员能够更好的利用资源,快速反馈测试过程中发现的问题。

当测试人员不跟随事先设定的脚本进行测试时,他们会在测试过程中积极学习被测系统的行为、表现和发现的缺陷,从而帮助他们提高测试能力,在测试过程中不断完善,帮助测试人员提出更好、更强大的测试。

探索性测试的挑战

由于不存在经过设计和评估的测试用例,“执行先于设计或设计与执行同时进行”的特点对探索性测试的风险管理等提出了巨大挑战。并不能完全替代剧本式测试。

  • 首先,探索性测试没有能力防止缺陷。剧本式测试可以在需求收集期间开始接入设计测试用例,从而尽早揭示缺陷;

  • 其次,当有规范的测试报告和测试文档要求时,剧本式测试的必要性则凸显出来;

  • 再者,探索性测试对测试结果的判断更多依赖于测试人员自身而不是需求文档、用户手册等标准,因此对于测试输出的正确性难以正确性评估;

  • 此外,考虑到我们经常需要反复执行相同的测试(如缺陷修复验证等),探索性测试缺乏可追踪或记录性的文档用例,使得回归测试变得困难。

当测试人员进行探索性测试时,偶然发现的缺陷较难复现。尤其是当开发人员要求详细的复现步骤时,测试人员关注于“复现缺陷”本身,而失去了探索性测试的意义。并且,测试的目的不只在于发现缺陷,还应该包含更多其他方面,例如功能覆盖等。因此,探索性测试并不能完全替代剧本式测试。

为了解决这些问题,有人提出了探索性测试和剧本式测试相结合的方法。例如,使用探索性测试设计新的测试,然后编写为脚本测试用例用于回归测试。

1)缺乏丰富的、专门的测试方法

高度依赖于测试人员的技能、经验和领域知识。当被测产品很复杂时,测试人员的技能和领域知识往往就会显得缺乏。虽然探索性测试允许测试人员在完成测试工作时自由地使用任何测试方法,并且允许联合多种测试方法(如风险测试、配对测试、需求测试、交叉功能测试等等)完成测试工作。但作为一门研究学科,对专门的测试方法研讨仍是不少研究人员关心的。

为了缓解这一挑战,James A.Whittaker提出的局部探索式测试方法从用户输入、状态、代码路径、用户数据和运行环境五个方面给予了测试人员方法指导,帮助测试人员更具方向性地探索软件细节。

并且“漫游”型全局探索式测试方法,更是广大学者讨论和研究。它将全局探索性测试比拟为城市旅游,从旅游者的角度出发探索软件的“商业区”、“历史区”、“娱乐区”等区域。

已有文献实验指出,将“漫游”探索性测试方法应用于敏捷开发项目,能够提高系统测试的有效性。此外,史亮、高翔所著的《探索式测试》一书,结合实际案例详细讲述了测试单个特性、交互特性和系统交互的探索性测试方法,例如可用于单个特性探索性测试的联想输入模型,可用于系统交互特性探索性测试的肥皂剧测试模型等等。

2)探索性测试发展自动化存在瓶颈

自动化测试是业界发展探索性测试的主要障碍之一。探索性测试偏重于手工测试,测试执行和测试结果的评估依赖于测试人员本身,难免存在正确性不稳定和高成本的问题。

而自动化测试可以帮助测试人员完成每天的回归测试或检查,减少软件开发的测试资源投入。对于测试结果的正确性评估也具有可靠性。相较于自动化测试的文档化管理,探索性测试测试需要知识和技能难以通过文档传递。

自动化是测试人员的工具之一,应该在任何合理的时候使用,以改进测试,并为其他类型的测试活动释放人力资源。再者,探索性测试和自动化不是相互排斥的,而是可以相互配合。因此,将探索性测试与自动化测试的可度量性和可重复性结合,不仅能提高探索性测试缺陷检测能力和有效性,还能节省时间和人力投入,提高探索性测试在回归测试中的可用性。

调查指出,测试人员在进行探索性测试时时常会同自动化测试结合。通过添加自动化的支持来增强经典的探索性测试方法,这使得整个过程在重复探索性测试期间更加可跟踪、透明并且成本更低。在此,我们可以将探索性测试的自动化发展方向称为“自动化探索性测试“,而业界研究较多的自动化探索性测试方法主要是将探索性测试方法与模型测试相结合。

例如:

  • 基于规则的R-BET模型,将基于规则的自动化测试与探索性测试结合用于GUI测试,提高了GUI测试的自动化回归测试能力和通用性缺陷、应用性缺陷检测能力;

  • MBET模型将ECGs模型与探索性测试结合,自动化生成测试用例,弥补了探索性测试的自动化缺陷和提高了单独使用模型测试服或探索性测试方法测试覆盖率;

  • ARME模型将手工进行缺失状态和转移路径的补全过程自动化,不仅可以扩展测试用例数量,提高探索性测试挖掘缺陷的能力,还能减少测试步骤的数量,使得测试套件变得更加有效;

  • 模型测试法与探索性测试法结合,为探索性测试添加自动化辅助,增强了探索性测试过程的可追溯性、透明性,减少了探索性测试重复测试的开销。

3)缺乏结构性的管理方法

探索性测试具有很强的自由度,但探索性测试也应该是有计划的、有组织的、有策略指导和目标导向的。

缺乏准备、组织和指导,测试人员可能会浪费大量的时间徘徊在相同功能的测试上,导致重复测试或忽略重要的测试,特别是当多个测试人员或测试团队都参与的时候。

此外,由于缺少测试计划和设计,难以单独跟踪某个功能测试覆盖率或单独对某个测试人员测试流程进行跟踪和管理。因此,缺乏结构性的管理方法是探索性测试的挑战之一。

为了缓解这一挑战,James Bach提出的著名的探索性测试管理方法——基于会话的测试管理(SBTM)。

SBTM反映了时间箱的概念,采用测试会话来跟踪测试(测试会话是基本的测试工作单元),而会话单中的测试章程则被许多实践者认为是SBTM的重要元素。

SBTM的出现,使探索性测试更加易于管理和指导测试人员使用探索性测试。它清晰地描述了测试目标和测试范围,被认为是一个高级的测试计划指导方法。提供了单个测试人员或团队测试人员之间相互协作的可能。

在其后的研究中,SBET探索性测试框架在SBTM框架上进行改进,封装了所有与会话相关的活动,加强了测试会话过程中的材料追溯(例如提供定制工具来控制、跟踪和支持ET会议。测试人员的所有操作都通过系统日志连续地记录在数据库中)。

TET(团队探索性测试会话管理)方法,将SBTM方法拓展到团队级别,邀请具有不同专业技能和知识背景的人员加入团队,从不同角度进行探索性测试,依靠团队协作提高了探索性测试的有效性和丰富了团队成员之间的相互学习和交流。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取 

有关不可替代的测试人:一文解释探索性测试是什么的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  8. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

    我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

  9. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  10. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

随机推荐