时间:2024-05-04
杨正卉
(四川大学软件学院,成都610065)
软件测试是保证软件质量和可靠性的主要手段。进行软件测试的主要方式分为手工测试和自动化测试。手工测试依赖于测试人员的直觉和经验,成本高,效率低。自动化测试可以在一定从程度上代替手工,极大的提高测试人员的效率。工业中经常使用的自动化测试工具主要有单元测试用例生成工具和符号化执行工具。单元测试用例生成工具根据一定的覆盖率准则自动生成可用的测试套件,通过执行测试套件来发现项目中的缺陷。符号化执行工具使用符号值代替程序的输入值执行程序,并使用约束求解器来判定路径的可行性。这些工具都以覆盖率作为测试标准。有研究者发现,测试用例的覆盖率是越高,捕获代码缺陷的可能性就越大。因此研究自动化单元测试用例生成工具生成的测试用例的覆盖率和检错率具有重要意义。
本文选择EvoSuite,Randoop 和JavaPathfinder 这三个具有代表性的工具为例,采用了基本数据结构代码,研究自动化单元测试工具的性能,拟回答以下问题:
(1)在基础数据结构代码上,EvoSuite、Randoop、JavaPathfinder 工具的代码覆盖率情况如何?
(2)什么因素影响了工具的性能?
近年来,一些论文表明测试用例的故障检测能力与其覆盖率直接相关[1-6]。其中,Gligoric 的研究表明分支覆盖是评估测试质量的最佳方法,但是当存在平局时,非循环的程序内路径覆盖可能更好[1]。Frankl 和Weiss 将分支覆盖率与def-use 覆盖率进行了比较,发现def-use 比分支覆盖更有效[2]。Gupta 比较了块,分支和谓词覆盖率,发现谓词覆盖率比其他测试覆盖率标准杀死了更多的突变体[3]。Namin 和Andrews 表明,测试套件的大小和覆盖范围与测试套件的有效性相关[4]。文献[5-6]表明测试用例的故障检测能力与其覆盖率直接相关。另外,单元测试用例生成工具EvoSuite,Randoop 以及符号化执行工具Javapathfinder 在前辈们的研究中也得到了广泛的应用。文献[7-9]主要涉及了单元测试用例工具EvoSuite 和Randoop。文献[7]探索了Randoop 和EvoSuite 在处理细微重构错误时的效率和局限性。列出了可能影响EvoSuite 和Randoop 工具生成有效套件的能力的一些限制。文献[8]使用LifeCalc工业应用程序的25 个实际故障来评估了EvoSuite,Randoop 自动生成的测试套件在检测实际故障方面的有效性。提供了从自动化单元测试生成应用到工业软件的一般经验教训。文献[9]将Java 自动化单元测试生成工具EvoSuite、Randoop、AgitarOne 应用于Defects4J数据集中的357 个故障,详细分析生成的套件在检测数据集中的故障时的执行情况。
文献[10-12]主要介绍了JavaPathfinder 工具的一些组件,及组件在工业中中的应用。文献[10]介绍了JavaPathfinder-AWT,它是Java PathFinder 软件模型检查器的扩展,可以有效地验证潜在的大型GUI 应用程序。JavaPathfinder-AWT 已经成功应用于大型NASA地面数据系统,发现了传统测试中的缺陷。文献[11]介绍了APEX 的模型检查工具的关键思想。实现了用户定义的属性类,以检查APEX 的每个状态。文献[12]提出了一种通过名为JavaPathfinder-Android 的模型检查工具来验证Android 应用程序的新方法。JavaPathfinder-Android 允许在Java PathFinder 上的Android 平台之外验证Android 应用程序。
为了回答上述研究问题,本文设计了比较实验,实验过程如图1 所示。在每个被测项目上,分别运用EvoSuite 和Randoop 生成3 组测试套件,并在该项目上执行这些测试套件,以这些测试套件结果的平均值作为工具对每个项目的覆盖率结果。直接在每个项目上执行JavaPathfinder 工具并获取相关的覆盖率结果。
图1 项目源代码覆盖率实验流程图
数据集是Data Structures and Algorithm Analysis 附录的基本的数据结构相关代码。该数据集包含了链表、队列、栈、二叉树、图,以及各类查找、排序算法。大部分项目给出了运用该项目代码的main 方法或者手工测试用例。在每个项目中,可能包含与其余项目相同的类。经过筛选,最终保留了79 个可用项目。
EvoSuite 工具应用遗传算法生成一组能最大化代码覆盖率的测试套件。它从一个随机测试用例的测试套件开始,然后迭代地应用搜索操作符,如选择、突变和交叉来发展它们。这种演进是基于覆盖率标准的适应性函数,默认的覆盖率标准为分支覆盖。一旦搜索结束,覆盖率最高的测试套件就会根据覆盖标准被最小化,在最小化测试用例时,向测试用例中添加回归测试断言。通过编译测试套件来检查每个测试是否具有语法上的有效性,通过执行来检查测试用例是否稳定。任何失败的断言都被EvoSuite 注释掉。在本次实验中,直接使用了EvoSuite 的默认参数配置。没有对工具参数做任何调整。
Randoop 工具实现了面向对象程序的反馈定向随机测试。这意味着它会迭代地扩展方法调用序列,随机选择候选对象,直到生成的序列产生未声明的异常或违反一般的代码契约。Randoop 还会执行生成的序列并创建断言来捕获正在测试的类的行为。然而,Randoop 不能针对特定的被测类,因为它使用了自底向上的方法,该方法需要方法调用的所有依赖项。因此,Randoop 需要在测试生成期间应用探索的所有类的列表作为输入。对于每个被测试的项目,需要提供一个文本文档,该文本文档中包含了被测项目中所有类的类名。因为Randoop 在默认情况下随机种子是确定的(例如随机的种子总是0)。因此每次生成测试用例时,需手动更改随机种子。在生成三组测试套件时,分别将随机种子设置为0,1,2。对于所有其他的设置,应用默认值。
JavaPathfinder 将符号执行与模型检查和约束解决结合在一起,用于在Java 程序中使用未指定的输入进行自动测试用例生成和错误检测。在这个工具中,程序是在表示多个具体输入的符号输入上执行的。变量的值被表示为来自对Java 字节码分析产生的约束。这些约束是利用现成的解决方案来解决的,以生成保证达到复杂覆盖标准的测试输入。通过解决各种覆盖义务的符号输入约束,JavaPathfinder 可以作为一个可定制的测试生成器。用户可以指定不同的代码覆盖率指标(例如mc/dc),它可以定制生成测试用例的搜索策略,并且可以以不同的格式保存测试,例如HTML 表格或JUnit 测试。此外,JavaPathfinder 还被用于在并行程序中生成反例。JavaPathfinder 工具参数设置如图2所示。
图2 JavaPathfinder工具参数配置
本实验使用EvoSuite 1.0.5.jar,Randoop 4.1.0 作为单元测试用例生成工具。符号化执行工具JavaPathfinder 核心组件JavaPathfinder-core 来源于http://babelfish.arc.nasa.gov/trac/JavaPathfinder。 运 用 工 具eclipse 在Windows 10 64 位操作系统下运行EvoSuite/Randoop 测试套件和JavaPathfinder 工具。在执行Evo-Suite 和Randoop 测试套件时运用了Junit 4 框架,使用EclEMMa 插件统计覆盖率。JavaPathfinder 工具为路径探测工具,不依赖于Junit,并且JavaPathfinder-core 自身便可以统计路径探测覆盖率。
(1)数据集处理
由于JavaPathfinder 运行时需要程序中有一个测试驱动。既main()方法。然而不是所有项目中都包含该方法,但绝大部分项目中都包含着特定的测试类。因此,处理数据集时,有如下四种情况:
若某个项目没有main()方法。则将该程序的测试类更改为普通类。将项目中继承Junit 框架的断言全部更改为Java 原生断言。在该类中手动增加一个public void main(String[]args)方法,在main 方法中调用原有的public void setup()方法及其他测试用例方法。
若某个项目中既有main()方法,又有测试类。则删掉原有的main()方法。则将该程序的测试类更改为普通类。将项目中继承Junit 框架的断言全部更改为Java 原生断言。在该类中手动增加一个public void main(String[]args)方法,在main 函数中调用原有的public void setup()方法及其他测试用例方法。
若某个项目有main()方法,没有测试类。则保留main()方法。不做任何更改。
若某个函数既没有main()方法,也没有测试类,则删除该项目。
(2)生成测试用例/运行JavaPathfinder
EvoSuite 和Randoop 都具有随机性,能够在每次调用时产生不同的结果。对于每个项目,EvoSuite 和Randoop 工具都生成3 组测试用例。JavaPathfinder 是符号化执行工具,每次执行结果一致,对于每个项目,JavaPathfinder 仅执行一次。
(3)测试用例执行与覆盖率统计
在执行EvoSuite 测试用例时,需要手动注释每个测试套件中有关EvoSuite 参数设置的内容。Randoop测试套件不需要进行任何更改,直接执行。执行时运用Eclemma 插件统计这两个工具对被测项目的覆盖率。由于JavaPathfinder 自带覆盖率统计功能,不需要使用额外的插件进行覆盖率统计。
在图3 中,EvoSuite 工具在方法覆盖率上较另外两个工具拥有明显的优势,它覆盖了被测项目中的绝大多数方法。仅个别被测方法未被覆盖。Randoop 工具在某些项目上的方法覆盖率也逼近100%,但在其余项目上,该工具的方法覆盖率并不是很理想。JavaPathfinder 工具的方法覆盖率远远低于其余两个工具。这是因为JavaPathfinder 工具执行时依赖于main 方法,到main 方法中的代码没有调用项目中某些方法时,该工具时无法覆盖率这些方法的。在图4 中,EvoSuite 工具的行覆盖依旧高于其余两个工具。Randoop 工具的行覆盖趋势图与方法覆盖率类似。这说明方法覆盖率严重影响了Randoop 工具的行覆盖率。当Randoop 工具覆盖到项目中的更多方法时,该工具的行覆盖率有望得到提升。在图5 中,EvoSuite 工具依旧占据着优势,该工具的字节码覆盖率依旧高于其余两个工具。但是,值得注意的是:JavaPathfinder 工具的字节码覆盖率居然在方法覆盖率和行覆盖率都低于Randoop 工具的前提下,获取了比Randoop 工具更高的字节码覆盖率。这说明了,当JavaPathfinder 工具执行了某个方法时,他可以执行该方法中更多的分支。从而访问到代表这些分支处理语言的字节码。
总的来说,EvoSuite 工具的方法覆盖率,行覆盖率和字节码覆盖率都高于其余两个工具。Randoop 工具的方法覆盖率和行覆盖率优于JavaPathfinder 工具,但JavaPathfinder 工具的字节码覆盖率优于Randoop工具。
图3 方法覆盖率对比
图4 行覆盖率对比
图5 字节码覆盖率对比
本文实验数据集中存在大量的静态字段或方法,Randoop 工具在生成测试用例的过程中没有回调被测系统的静态状态,产生了大量的不稳定测试。不稳定测试导致工具提前终止运行或者无法生成测试用例。这是影响Randoop 工具覆盖率的重要原因之一。在本文中,当使用Randoop 工具进行实验时,有19 个项目产生了flaky tests。例如项目Ch3、Mem、Preorder 等,这几个项目因为Randoop 在生成过程中产生了flaky test,导致生成过程过早中断,未生成可执行的测试套件。因此覆盖率图中存在断点现象。另外,即使Randoop 工具生成了可执行的测试套件,该套件的测试用例数量会明显少于实际需要的数量,所生成的测试用例不能覆盖到项目中剩余部分的方法。
另外,本文实验数据级中存在着大量的代码冗余现象。例如,项目中的某些方法永远不会被调用。当我们使用EvoSuite 和Randoop 工具对被测项目生成测试套件时,这两个工具是以一定的覆盖率准则来判断是否继续生成测试用例,因此不管项目中的方法是否是冗余的,只要它存在于项目中,这两个工具都努力去生够覆盖该方法的测试用例。但对于JavaPathfinder工具来说,冗余代码代表着无论该工具模拟怎么的数据也无法执行到这些方法中所包含的代码。这严重影响了JavaPathfinder 工具执行时的覆盖率结果。
(1)内部有效性
实验中对于EvoSuite 及Randoop 两种工具上大都采用默认参数设置。Randoop 工具仅仅在随机种子方面进行了设置。EvoSuite 工具在运行时从一个随机测试用例的测试套件开始,然后迭代地应用搜索操作符来搜索和发展测试用例。一旦搜索结束,覆盖率最高的测试套件的覆盖率就会根据覆盖标准被最小化,于此同时,向测试用例中添加回归测试断言。在最小化测试套件的过程中,会出现超时现象。这种现象会导致个别项目中的某些类测试用例没有写入成功。在一定程度上,可能会导致该项目覆盖率偏低。
(2)外部有效性
本文主要基于基本的数据结构代码进行研究。代码比较简单,每个项目中的类文件个数低于10 个。并且每个类文件中项目代码行数不超过300 行。在相同类型的数据结构代码中运用到了其余项目的部分代码。项目之间的耦合程度偏高,独立性不够。对于实际项目的模仿力度不够。但数据结构也是在日常项目中用到的最多的知识,数据结构代码具有公共性。若自动化测试工具能该数据集中取得很好的成果,那么这些工具在实际项目中的表现不会很差。
本文通过覆盖率实验分析了自动化测试工具Evo-Suite、Randoop 和JavaPathfinder 在基本数据结构代码上的覆盖率表现。实验结果表明:①EvoSuite 工具对被测项目的覆盖能力明显优于另外两个工具。尤其时方法覆盖率(高达90%,甚至100%)。Randoop 工具的方法覆盖率和行覆盖率优于JavaPathfinder 工具,但JavaPathfinder 工具的字节码覆盖能力明显优于Randoop 工具。②实验数据集的特性影响着被测工具的性能。例如:静态字段导致Randoop 工具提前终止运行;冗余代码无法被JavaPathfinder 工具所执行。本文仅分析了自动化测试工具对被测系统的代码方法覆盖率、字节码覆盖率、行覆盖率情况。未来,可以研究它们各自的分支覆盖,谓词覆盖情况。另外可以考虑将各种工具的覆盖能力与缺陷查找能力结合分析。对于EvoSuite 和Randoop 这两个单元测试用例生成工具,我们还可以研究这两个工具所生成的测试套件的覆盖率和变异分数之间的关系。另外,在本文实验中,没有很好的处理静态字段对Randoop 工具的影响。在将来继续研究如何使如何优化Randoop 工具,使之生成尽可能少的不稳定测试。
我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自各大过期杂志,内容仅供学习参考,不准确地方联系删除处理!