软件测试有50年的历史,自动化测试的历史也在20年以上。尽管如此,端到端的、彻底的自动化测试仍然是一个十分难以企及的梦想。
测试执行的自动化易,测试生成的自动化难,测试判定(oracle)的自动化则难上加难。可以说,我们基本上仍停留在测试执行的自动化阶段。文章源自玩技e族-https://www.playezu.com/193597.html
没有测试生成和测试判定的自动化,就没有端到端、彻底的自动化测试。要实现端到端、彻底的自动化测试,需要我们突破测试生成和测试判定自动化这两大瓶颈。文章源自玩技e族-https://www.playezu.com/193597.html
在众多的探索中,随机测试是一种原理简单,某些情况下效果还行,实际应用比较成功的一种技术。今天,我们就来聊聊随机测试,探讨下它在测试领域的"江湖地位"。文章源自玩技e族-https://www.playezu.com/193597.html
那么,什么是随机测试呢?文章源自玩技e族-https://www.playezu.com/193597.html
我们知道,抽象地说,软件测试就是构造输入(input),并作用于被测系统(SUT),然后观察其实际输出(actual uutput),并与期望输出(expected output)进行比较的过程。文章源自玩技e族-https://www.playezu.com/193597.html
随机测试,就是用随机的方式产生测试输入(input)。不同的测试上下文,测试输入不同,随机测试的具体形式也就不同。举几个例子,让我们对随机测试有一个直观的认识。文章源自玩技e族-https://www.playezu.com/193597.html
文章源自玩技e族-https://www.playezu.com/193597.html当被测对象是函数时,测试输入是函数的入参,随机测试就是随机生成入参;当测试对象是API接口时,测试输入是请求消息,随机测试就是随机生成请求消息;当测试对象是用户界面时,测试输入是操作序列,随机测试就是随机生成操作序列。文章源自玩技e族-https://www.playezu.com/193597.html
如何进一步认识随机测试呢?文章源自玩技e族-https://www.playezu.com/193597.html
第一,随机测试不需要我们"掷骰子"。依靠各种编程语言自带的伪随机数生成器,我们可以很容易自动化地随机生成测试输入。因此在随机测试中,测试输入的生成是自动化的。文章源自玩技e族-https://www.playezu.com/193597.html
第二,随机测试的有效性是一个概率问题。从理论上说,经过足够长的时间,随机产生的测试输入可以覆盖完整的输入空间。从实际上说,我们会平衡有效性与效率。这意味着,当资源投入有限时,我们不能期望随机测试有很高的覆盖率。
第三,随机测试会产生大量的测试输入和输出。如何自动化地判定测试结果(生成期望输出并与实际输出作比较)?这依然是随机测试无法解决的瓶颈。
带着对随机测试的这三点认识,我们来看看随机测试是如何在工业界得到实际应用的。
Fuzzing测试
随机测试最经典的应用就是Fuzzing。针对一个被测对象,Fuzzing通常不对它有任何假设,而是随机生成无以计数的、合法和非法的输入,然而观察被测对象的行为。
为了解决测试判定自动化问题,Fuzzing采用一种松弛或者隐性的oracle。例如,以被测对象是否发生crash作为测试是否失败的判定标准。这意味着Fuzzing很难用来做功能测试,而一般只用来做可靠性和安全测试。
Fuzzing的劣势是效率低。根据经验,一个包含成百上千个自动化用例的CI任务,执行时间超过半小时,大家就不能忍了。而一个Fuzzing任务,执行几天几夜都不稀奇。
近年来,为了提高Fuzzing的有效性和效率,基于动态反馈的新一代Fuzzing技术诞生了,代表性工具就是AFL。
本质上说,新一代Fuzzing采用的是智能的随机测试。我们知道,传统的随机测试,前后执行的测试是相互独立的。而智能的随机测试,会利用前面的执行结果来引导后面的测试输入的产生和测试的执行。
Monkey测试
熟悉安卓测试的同学应该都了解安卓有一个自带的Exerciser Monkey。它通过产生随机的用户事件和系统事件,来观察应用是否发生crash或抛出未处理的异常。
从本质上说,Fuzzing和Monkey是同一样东西。如果非要说它们有区别,那区别就在于:Fuzzing强调的是测试数据(data)的随机生成,而Monkey测试强调的是测试动作(action)的随机生成。
在UI和APP的随机测试中,我们经常使用Monkey的概念。包括一些基于机器学习的UI智能化测试工具,也叫做Monkey。这时候,Monkey用来表示对用户操作,例如触摸、点击、滑动等的模拟。
混沌测试
软件测试是不完备的。对于对可用性要求极高的系统来说,仅仅通过正向的测试和验证,往往存在遗漏。为此,人们发明了混沌测试(Chaos Testing)。它通过故意向系统注入错误,来测试系统是否发生崩溃,从而构建错误容忍的系统。
混沌测试的核心在于故障注入。如何有效注入故障呢?随机测试的思想大量运用于此。在混沌测试鼻祖Netflix的工具箱中,就有许多基于随机算法生成各种异常和错误的Monkey工具。例如,Latency Monkey可以通过随机化服务端的响应时延来模拟服务宕机、服务降级等异常现象。
基于属性的测试
无论Fuzzing、Monkey还是Chaos,都只使用十分松弛的oracle(例如,系统是否发生crash)来实现自动化测试结果判定。这制约了它们只能适用于可靠性、安全等"小众"测试领域。
软件测试的最大板块还是在功能测试。功能测试的oracle自动化是世界级难题。近年来,基于属性的测试(Property-based Testing)横空出世,在局部突破了功能测试oracle自动化瓶颈。
什么是属性?属性就是事物的固有性质。在功能测试中,它就是被测对象不随测试输入(外部条件)变化而发生改变的性质。
举例来说,一个字符串拼接函数,对于任何输入字符串a和b,拼接结果一定包含字符串b,这就是属性;一个编解码模块,给定任意输入X,对其编码结果进行解码,结果一定是X,这也是一个属性。
在传统测试中,在测试开始前,先要构造测试输入样本。而基于属性的测试就不需要绞尽脑汁设计样本,只需要识别出属性。在测试执行过程中,相应的测试框架会随机产生任意的期望输入数据,并检验对每一个输入,属性是否保持不变。
基于属性的测试能够让一个用例自动变成N个用例,并且实现了部分的功能校验。针对各种编程语言的基于属性的自动化测试框架,正在如火如荼地开发之中。例如,针对Python的Hypothesis,针对Javascript的fast-check。
随机测试,一个多么朴素的方法,却成功在业界得到广泛应用,帮助我们实现局部的、特定情形下的完全自动化测试。
另一方面,随机测试的深入研究也在继续,包括非均匀分布的随机测试、自适应随机测试,以及上面提到的反馈引导的随机测试等。这些研究有望在未来进一步提高随机测试的能力。