软件测试是发现软件缺陷的过程, 是保障软件质量的重要手段. 由于被测系统的复杂性以及测试成本的限制, 软件测试自动化已经成为必然. 随着测试技术的进一步发展, 测试流程的不断规范, 大量的单元测试用例生成工具涌现. 这些工具以覆盖率作为测试标准. 有研究者发现, 测试用例的覆盖率是越高, 捕获代码缺陷的可能性就越大. 研究自动化单元测试用例生成工具生成的测试用例的覆盖率和检错率具有重要意义, 直接影响着这些测试工具是否能在业界发挥更好的作用. 在国际上的一些单元测试工具竞赛中[1-6], Evosuite多次获得第一名; 在2019年的竞赛中, Randoop工具和手工编写的测试用例被作为其余工具对比的基线. 因此, 本文选择Evosuite, Randoop两个具有代表性的工具为例, 采用了2016年单元测试工具竞赛中使用的数据集: Defects4J, 研究自动化单元测试工具的性能, 拟回答以下问题:
(1)在不同的生成时间下, Evosuite, Randoop生成的测试用例在数量上、方法覆盖率、分支覆盖率等方面的表现如何?
(2)哪些因素影响了Evosuite、Randoop工具生成的测试用例的覆盖率?
2 相关研究为了分析自动化测试用例生成工具的性能, Fraser等进行了手工测试和自动化测试的对比实验, 邀请49名参与者, 从多个方面分析了手工测试用例和自动化工具Evosuite所生成的测试用例的特定[7]. Shamshiri等在Defects4J数据集上研究了Evosuite、Randoop工具, 以及商用工具AgitarOne的性能, 回答了如何使自动化单元测试工具能够查找到更多的缺陷的问题[8]. Almasi等使用工业界的项目——LifeCalc对Evosuite和Randoop工具的有效性进行了评估[9]. Kotelyanskii等对基于搜索的单元测试用例工具Evosuite的参数调优进行了研究, 其研究结果为本文提供了参考[10]. 在这些研究中, 主要关注的是工具的缺陷查找能力, 但作为自动化单元测试, 更重要的是其对代码的覆盖能力, David Schuler 确定了覆盖率是判断测试用例质量的一个可靠指标, 覆盖率越高, 缺陷查找率可能越高[11]. 本文详细分析了工具生成的测试用例对源代码的方法和分支的覆盖率, 进一步探索了工具的特点和存在的缺陷.
3 实验方法为了回答上述研究问题, 本文设计了比较实验, 实验过程如图1所示. 应用自动化单元测试用例生成工具Evosuite和Randoop, 在Defects4J数据集上, 针对不同的被测软件, 分别生成测试用例. 工具生成测试用例的时间分别设置为10 s, 20 s, 30 s, 60 s, 120 s, 180 s, 300 s. 由于Evosuite和Randoop都是随机生成测试用例, 每次产生的结果有一定随机性. 因此, 在实验设计上, 同一生成时间重复运行Evosuite和Randoop 3次, 并以3次运行结果的平均值作为实验分析数据.
3.1 Randoop和Evosuite工具
Randoop[12]是基于反馈的面向对象单元测试的随机测试用例生成工具, 对于被测类, Randoop创建一系列方法调用和构造函数, 依次创建和更改对象状态. 执行所有创建的序列, 并用执行的生成来创建捕获系统行为的断言. Randoop的输入是一组要测试的Java类, 一个时间限制和一组可选的规范检查器, 生成是一组对应的JUnit测试用例. Randoop的随机种子在缺省情况下为0, 对于相同被测类, 每次运行时需更改随机种子的值. 另外, Randoop工具在生成过程中易产生片状测试, 导致Randoop工具无法生成更多的测试用例. 为了解决该问题, 需将flaky-test-behavior参数设置为OUTPUT.
Evosuite[13]是基于搜索的单元测试用例生成工具, 该工具被集成于Eclipse, IntelliJ, Maven等环境下, 可以生成Java类的JUnit测试用例. 该工具采用遗传算法, 从一组随机测试用例开始, 迭代地应用选择、突变和交叉等搜索操作符来进化测试用例, 进化由基于覆盖标准的适应度函数指导, 一旦搜索结束, 测试用例就会被最小化, 并添加测试断言. 在本实验中, 除了禁止Evosuite使用单独的类加载器, 更改生成时间以外, 其余均采用默认配置.
3.2 被测软件实验被测软件采用Defects4j数据集[14]中的5个开源项目的多个版本. 这些项目版本中有357个真实缺陷: JFreeChart (26个缺陷)、Google Closure compiler (133个缺陷)、Apache Commons Lang (65个缺陷)、Apache Commons Math (106个缺陷)和Joda Time(27个缺陷). Apache Commons Lang是处理Java基本类方法的工具类包, 弥补了Java.Lang API基本处理方法上的不足; Apache Commons Math是轻量级自容器的数学和统计计算方法类包, 包含大多数常用的数值算法, 外部依赖较少. JFreeChart是为Applications, Applets, Servlets以及JSP等使用所设计的, 可生成多种图表, 产生PNG和JPEG格式的生成. 对于项目版本的每一个缺陷, Defects4j提供了: (1)该缺陷对应的缺陷版本和已修复版本; (2)开发人员手工编写的可以揭露缺陷的测试用例; (3)缺陷类的列表.
本实验选择JFreeChart, Apache Commons Lang, Apache Commons Math作为被测软件, 既满足了多样性的需求, 也保证了所选择的软件的代表性. 实验将3个软件每个版本中已修复缺陷的类作为被测类. 当某个类在缺陷类列表中多次出现时, 使用该类第一次出现缺陷时对应的已修复版本. 本实验共筛选出71个被测类.
3.3 实验环境本实验在Windows 64位操作系统下采用SUN Java 8 SE, Eclipse Europa IDE编译被测项目. 在JUnit 4框架下运行测试用例, 使用EclEmma插件计算测试用例覆盖率. 编程自动统计覆盖率结果, 并采用Excel工具对结果进行分析.
3.4 实验步骤(1)编译被测类
Apache Commons Lang和Apache Commons Math是Maven构建的项目, 编译该项目时, 需确保Eclipse中已配置Maven环境.
(2)测试用例生成
在命令行中使用Evosuite-1.0.5.jar和Randoop-4.0.4生成测试用例. 在生成测试用例时, 生成时间设为: 10 s, 20 s, 30 s, 60 s, 120 s, 180 s, 300 s. Evosuite和Randoop工具分别在每个生成时间下运行3次. 每次运行时, Evosuite参数保持不变. Randoop随机种子分别设置为123, 234, 456, 其余参数保持不变.
(3)测试用例执行与覆盖率计算
测试用例在Eclipse中使用JUnit 4框架分别执行. 测试覆盖率由eclEMMA插件统计, 结果在测试用例执行结束后得到. Randoop生成的测试用例分为回归测试用例和揭错测试用例, 这两种测试用例都应被执行. 考虑测试的稳定性, 若测试用例不能执行或执行中断, 则丢弃该测试用例.
本实验从以下几个角度统计覆盖率结果:
① 以类为单位, 统计Evosuite和Randoop测试用例对被测类的方法覆盖率和分支覆盖率.
② 以方法为单位, 统计Evosuite和Randoop测试用例在每个方法中的分支覆盖率.
(4)实验结果处理
实验中存在无法生成测试用例或测试用例无法正常运行的情况, 比如个别类或者方法不存在分支, 此时默认分支覆盖率为100%.
4 实验结果及分析 4.1 Evosuite和Randoop生成测试用例的数量、方法覆盖率、分支覆盖率(1) Evosuite和Randoop工具生成的测试用例数量.
图2展示了Evosuite和Randoop工具生成的测试用例数量与生成时间的关系. 无论生成时间如何设置, Evosuite工具生成的测试用例数量相对稳定. Randoop工具生成的测试用例数量随着生成时间的增加而增加. 当生成时间为60 s时, 明显看出Randoop测试用例数量多于Evosuite. 图3展示了在不同项目中 Evosuite和Randoop生成的测试用例数量对比, 当生成时间为10 s, 20 s时, Randoop生成的测试用例数量依旧高于Evosuite. 因此在所有生成时间设置下, Randoop工具生成的测试用例数量都多于Evosuite. 因为Evosuite工具会对生成的测试用例进行最小化操作, 保留符合覆盖率标准的测试用例. 而Randoop工具则输出生成的所有测试用例.
(2) Evosuite和Randoop工具生成的测试用例的方法覆盖率.
图4展示了Evosuite和Randoop工具生成测试用例对被测类的方法覆盖率与分支覆盖率. 在3个被测软件中, Evosuite测试用例的方法覆盖率随着生成时间的增加而增加. Randoop测试用例的方法覆盖率在生成时间为10~120 s时存在递增现象, 并在120 s时达到最大值. 在Chart项目中, 当生成时间为300 s时, Randoop测试用例的方法覆盖率有所降低; 在Lang项目上, 当生成时间超过120 s后, Randoop测试用例的方法覆盖率保持平稳; 对于Math项目, 当生成时间超过120 s后, Randoop测试用例的方法覆盖率逐渐降低. 当且仅当生成时间为10 s时, 对于Chart项目, Randoop测试用例的方法覆盖率高于Evosuite. 当生成时间达到20 s后, 无论在哪个项目上, Evosuite测试用例的方法覆盖率都高于Randoop. 总体来看, Evosuite测试用例的方法覆盖率高于Randoop.
(3) EvosuiteRandoop工具生成的测试用对被测类的分支覆盖率.
在Chart和Math项目中, Evosuite工具生成的测试用例对被测类的分支覆盖率随着生成时间的增加逐渐上升. 在Lang项目中, 当生成时间为30 s时, Evosuite工具生成的测试用例的分支覆盖率较生成时间比20 s时略有降低. 但在其余生成时间上, Evosuite工具生成的测试用例的分支覆盖率依旧随着生成时间的增加而增加. 当生成时间为10~120 s时, Randoop工具生成的测试用例的分支覆盖率随着生成时间的增加而增加. 当生成时间达到120 s后, 在Chart和Lang项目上, Randoop工具生成的测试用例的分支覆盖率达到最优值, 并保持稳定. 在Math项目上, Randoop工具生成的测试用例的分支覆盖率逐渐下降. 当且仅当生成时间为10 s时, 在Chart项目上Randoop工具生成的测试用例分支覆盖率与Evosuite工具生成的测试用例分支覆盖率相当, 当生成时间达到20 s后, Evosuite测试用例的分支覆盖率高于Randoop测试用例. 整体来看, Evosuite对被测类的分支覆盖率同样优于Randoop.
(4)影响Evosuite对被测类的分支覆盖率高于Randoop的因素.
从上述结论可知, Evosuite对被测类方法覆盖率高于Randoop. 原因之一是Randoop工具的随机性, 不能覆盖部分方法, 就不能覆盖到方法中的分支, 而Evosuite可实现更多的方法覆盖, 可覆盖到方法中的分支. 图5展示了同时被Evosuite和Randoop测试用例覆盖的方法的分支覆盖率. 从图5可见, 当Evosuite和Randoop同时覆盖某个方法时, Evosuite测试用例对该方法的分支覆盖程度高于Randoop. 因此, Evosuite对被测类分支覆盖率高于Randoop不仅因为Evosuite覆盖了更多的方法, 还因为Evosuite测试用例较Randoop可以覆盖到方法中的更多分支.
4.2 影响Evosuite和Randoop工具生成的测试用例覆盖率的因素
通过分析被测软件, 发现软件版本中存在抽象类, 而Randoop工具在执行时, 会忽略这些抽象类以及接口. 所以, 即使抽象类中存在着具体的方法(例如: Math_12, Lang_15等), Randoop工具也没有对这些方法生成测试用例. 可以认定, 被测软件中存在的抽象类是影响Randoop工具生成的测试用例覆盖率的因素之一. 在Randoop工具改进中, 建议考虑对抽象类的处理.
图6展示了Randoop工具所生成的一个测试用例的样例. 在执行该用例时, 发现深色标注的代码未执行, 从而未覆盖该代码所调用的被测方法. 从这一测试用例中可以看出, Randoop在生成测试用例时, 随机模拟被测方法中所需要的参数来调用方法. 当Randoop工具不确定测试代码是否能成功执行时, 将会以try-catch语句包裹该代码. 实验证明, 若测试用例中某些语句被try-catch包裹, 则该语句基本无法正常执行, 从而导致无法覆盖其中被调用的被测方法. 因为Randoop不能成功模拟调用该被测方法所需要的参数. Randoop的设计者也提到:目前Randoop对于参数的选择是随机的. 因此, 有效模拟被测方法所需参数是影响Randoop覆盖率的因素之一. 当Randoop需要模拟某些参数来调用方法时, 可以依照上下文关系获取该参数的值, 而不是重新定义随机值[15].
在图4中, 当生成时间足够长时, Evosuite测试用例的方法覆盖率达到90%以上, 方法覆盖程度并不是影响该工具分支覆盖率的重要因素. 图7展示了 Evosuite工具对某被测模块的覆盖情况的样例, Evosuite工具所生成的测试用例虽然覆盖了该方法, 但当方法中出现分支时, Evosuite无法覆盖该分支中所包含的语句, 该分支的运行依赖于其他方法的执行结果. 因此, Evosuite工具对被测方法相关的依赖方法结果的构造是影响分支覆盖的因素之一.
4.3 有效性分析
内部有效性, Evosuite和Randoop工具都具有一定的随机性, 若要评估工具测试用例的覆盖率, 应生成尽可能多的测试用例. 本实验在多个项目、多个版本、多个生成时间上分别生成3组测试用例, 实验数据具有一定的统计意义. 在实验过程中, Randoop工具每次运行所生成的测试用例的方法覆盖率和分支覆盖率波动较小; Evosuite覆盖率波动情况较Randoop更大, 若以工具的最优值作为衡量标准, Evosuite测试用例的覆盖率结果将更优. 但就Evosuite运行情况来看,采用最小值、最大值、平均值完整体现工具的真实情况, 衡量工具覆盖率更为合理.
外部有效性, 从实验结果来看, Evosuite工具生成的测试用例的方法覆盖率和分支覆盖率随着生成时间的增加而增加. Randoop工具在生成时间达到120 s后, 对于不同的项目方法覆盖率和分支覆盖率具有不同的特征. 但Lang和Math项目是Apache Commons中的基础工具包, 使用频率较高, Chart项目充分体现了绘图程序的基本特征, 因此, 本实验选择的被测项目既满足了多样性的需求, 也保证了所选择的软件的代表性.
5 结论本实验通过对Evosuite和Randoop工具所生成的测试用例的覆盖率分析发现: ① 即使生成时间低至10 s, Radnoop所生成的测试用例数量依旧高于Evosuite. 并且当生成时间到达20 s后, 单元测试工具Evosuite生成的测试用例的方法覆盖率和分支覆盖率覆盖率高于Randoop. ② 影响Randoop工具覆盖率的原因包含如何处理抽象类以及如何模拟被测方法所需要的参数等. Evosuite工具提高覆盖率的关键问题是如何构造与被测方法相关的依赖方法结果, 以保证能够覆盖更多的分支. 在未来的工作中, 可以研究是否可以采用符号化执行与随机测试相结合的方法提高Randoop工具的参数模拟能力.
[1] |
Fraser G, Arcuri A. Evosuite at the SBST 2013 tool competition. Proceedings of 2013 IEEE Sixth International Conference on Software Testing, Verification and Validation Workshops. Luxembourg City, Luxembourg. 2013. 406–409.
|
[2] |
Fraser G, Arcuri A. Evosuite at the SBST 2015 tool competition. Proceedings of 2015 IEEE/ACM 8th International Workshop on Search-Based Software Testing. Florence, Italy. 2015. 25–27.
|
[3] |
Fraser G, Arcuri A. Evosuite at the SBST 2016 tool competition. Proceedings of 2016 IEEE/ACM 9th International Workshop on Search-Based Software Testing (SBST). Austin, TX, USA. 2016. 33–36.
|
[4] |
Fraser G, Rojas JM, Campos J, et al. Evosuite at the SBST 2017 tool competition. Proceedings of 2017 IEEE/ACM 10th International Workshop on Search-Based Software Testing (SBST). Buenos Aires, Argentina. 2017. 39–42.
|
[5] |
Fraser G, Rojas JM, Arcuri A. Evosuite at the SBST 2018 tool competition. Proceedings of 2018 IEEE/ACM 11th International Workshop on Search-Based Software Testing (SBST). Gothenburg, Sweden. 2018. 34–37.
|
[6] |
Campos J, Panichella A, Fraser G. Evosuite at the SBST 2019 tool competition. Proceedings of 2019 IEEE/ACM 12th International Workshop on Search-Based Software Testing (SBST). Motreal, QC, Canada. 2019. 29–32.
|
[7] |
Fraser G, McMinn P, Staats M, et al. Does automated white-box test generation really help software testers? Proceedings of the 2013 International Symposium on Software Testing and Analysis. New York, NY, USA. 2013. 291–301.
|
[8] |
Shamshiri S, Just R, Rojas JM, et al. Do automatically generated unit tests find real faults? An empirical study of effectiveness and challenges (T). Proceedings of the 30th IEEE/ACM International Conference on Automated Software Engineering. Clayton, Australia. 2015. 201–211.
|
[9] |
Almasi MM, Hemmati H, Fraser G, et al. An industrial evaluation of unit test generation: Finding real faults in a financial application. Proceedings of 2017 IEEE/ACM 39th International Conference on Software Engineering: Software Engineering in Practice Track (ICSE-SEIP). Buenos Aires, Argentina. 2017. 263–272.
|
[10] |
Kotelyanskii A, Kapfhammer GM. Parameter tuning for search-based test-data generation revisited: Support for previous results. Proceedings of 2014 14th International Conference on Quality Software. Dallas, TX, USA. 2014. 79–84.
|
[11] |
Schuler D, Zeller A. Assessing oracle quality with checked coverage. Proceedings of 2011 Fourth IEEE International Conference on Software Testing, Verification and Validation. Berlin, Germany. 2011. 90–99.
|
[12] |
Pacheco C, Ernst MD. Randoop: Feedback-directed random testing for Java. Companion to the 22nd ACM SIGPLAN Conference on Object-Oriented Programming Systems and Applications Companion. New York, NY, USA. 2007. 815–816.
|
[13] |
Fraser G, Arcuri A. Whole test suite generation. IEEE Transactions on Software Engineering, 2013, 39(2): 276-291. DOI:10.1109/TSE.2012.14 |
[14] |
Just R, Jalali D, Ernst MD. Defects4J: A database of existing faults to enable controlled testing studies for java programs. Proceedings of the 2014 International Symposium on Software Testing and Analysis. New York, NY, USA. 2014. 437–440.
|
[15] |
Randoop project ideas. https://randoop.github.io/randoop/projectideas.html. [2020-01-09].
|