四时宝库

程序员的知识宝库

软件开发过程中单元测试生成(单元测试对应软件开发哪个阶段)

论文:Arcuri A, Campos J, Fraser G. Unit test generation during software development: Evosuite plugins for maven, intellij and jenkins[C]//2016 IEEE International Conference on Software Testing, Verification and Validation (ICST). Institute of Electrical and Electronics Engineers, 2016: 401-408.

https://ieeexplore.ieee.org/document/7515498

论文摘要

为面向对象类自动化生成单元测试的不同技术已经被提出来了。但如何将这些工具集成到软件开发的日常活动中是一个小问题。在本文中,我们报告了我们在支持工业合作伙伴在其软件开发过程中引入EVOSUITE自动化Junit测试生成工具时的经验。第一步是为Apache Maven构建提供插件。从面向研究的点击工具转变为构建过程的自动化步骤会影响开发人员如何与工具交互并生成测试,因此,我们为当下流行的IntelliJ集成开发环境(IDE)生成了一个插件。构建自动化是持续集成(CI)的核心组件,我们为Jenkins CI系统提供了另一个插件,允许开发人员监控EVOSUITE的结果并在源代码树中集成生成的测试。在本文中,我们将讨论插件的最终体系结构,以及构建此类插件时出现的挑战。尽管所描述的插件是针对EVOSUITE工具的,但它们可以进行调整,其架构也可以重复用于其他测试加工工具。

研究介绍

EVOSUITE工具自动为Java软件生成JUnit测试。给定被测试类(CUT),EVOSUITE创建调用序列,将测试标准最大化,如行和分支覆盖,同时生成JUnit断言以捕获CUT的当前行为。开源项目和工业系统的实验表明,EVOSUITE可以成功地实现良好的代码覆盖,但是如何将其集成到软件工程师的开发过程中呢?

为了回答这个问题,测试生成工具和软件开发人员之间的相互作用已经经过研究和观察。但是,将测试生成集成到开发过程中的问题超出了单个开发人员与工具的交互:在工业环境中,一些开发人员在相同的大型代码库上工作,并且测试生成工具应该顺利地集成到软件工程师的当前流程和工具链。

有不同的方法来访问测试生成工具。来自命令行的第一个选项是运行EVOSUITE。如果该工具在独立的可执行jar中编译和汇编,则可以在CUT(例如org.Foo)上调用它,如下所示:

	java -jar evosuite.jar org.Foo

但是,在典型的Java项目中,需要指定完整的类路径(例如,作为另一个命令行输入)。这有必要告诉工具在哪里找到CUT的字节码及其所有依赖类。例如,如果目标项目是在名为build的文件夹中编译的,那么在EVOSUITE中可以使用:

	java -jar evosuite.jar -class org.Foo -projectCP build

其中选项-class用于指定CUT,选项-projectCP用于指定类路径。

如果在“静态”上下文中使用EVOSUITE,例如,当类路径没有改变时,并且用户多次测试相同的特定类集合,则该方法可以正常工作。这种情景的典型例子是在学术背景下对一组基准进行实验[5] - 这与工业用例完全不同。工业软件系统在类路径中可能有数百个(如果不是数千个)条目,当开发人员将新的更改推送到源存储库(例如,Git,Mercurial或SVN)时,这些条目可能经常发生变化。为每个子模块手动指定类路径不是一个可行的选择。

将测试生成工具直接集成到IDE中,可以提高可用性。例如,EVOSUITE有一个Eclipse插件,其中包含一个jar版的EVOSUITE。开发人员可以通过选择单个类来激活测试生成,并且类路径直接来自IDE本身的API。但是,这种方法不适用于具有许多类和频繁更改的大型项目。此外,EVOSUITE要求对软件项目的所有开发人员必须保持一致的构建设置进行更改,因为EVOSUITE对CUT环境的模拟需要包含依赖性jar文件(包含用于例如文件系统的Java API和网络的模拟基础结构)。

为了克服这些问题,我们为工业Java项目中的通用软件开发基础架构开发了一套插件。特别是,在本文中,我们提供了一个插件来控制来自Apache Maven1的EVOSUITE,以及IntelliJ IDEA2和Jenkins CI3的插件,以便与Apache Maven插件进行交互。

自动化构建中生成单元测试:

如今,编译和组装Java软件的工业通用标准是使用自动构建工具。 Maven可能是目前最受欢迎的一个(较老的一个是Ant,而最近的Gradle目前正在获取推动力)。将单元测试生成工具集成到自动构建工具中包括支持生成的测试的执行以及新测试的生成。

A. 在Maven中集成生成的测试:

为了使测试具有确定性并将它们与环境隔离,EVOSUITE需要包含运行时库。使用像Maven这样的构建工具时,很容易添加第三方库。例如,可以轻松添加(并自动下载)EVOSUITE生成的测试的运行时依赖性,例如通过将以下条目复制并粘贴到定义构建的pom.xml文件中:

设置完成后,生成的测试可以使用此库,该库现在是类路径的一部分。这很重要,因为当编译和打包软件项目时(例如,使用命令mvn package),所有测试用例也会被执行以验证构建。

然而,当我们第一次为我们的一个工业合作伙伴生成测试用例时,构建目标项目变成了一片混乱:一些生成测试失败,以及一些现有的手动测试(即有软件工程师人工编写的JUnit测试),打破了构建。原因在于如何对类进行检测:EVOSUITE生成的测试激活Java代理以执行运行时字节码检测,这需要用我们的mocks替换一些Java API类。在运行测试时完成检测,并且只能在第一次加载类时完成。一方面,如果在EVOSUITE之前首先运行手动的现有测试,则CUT的字节码将已经加载,并且不能进行检测,让它使所有生成的测试失败。另一方面,如果最后运行手动测试,它们将使用已检测的版本,并且可能会失败,因为没有为它们配置模拟环境。

可能有不同的方法来处理此问题,例如强制这些不同的测试集在独立生成的JVM上运行。但是,这可能会给软件工程师带来一些负担,他们需要执行配置,并调整用于报告和可视化测试结果的所有其他工具。我们的解决方案有两个:(1)我们的每个mocks都有一个回滚功能,它在测试完成后自动激活,因此在运行后进行手动测试生成不再是问题; 我们为Maven测试执行器创建了一个监听器,它在运行任何测试之前强制加载和检测所有CUT,包括手动测试。有了这个解决方案,工程师可以按任何顺序运行所有测试,并在同一个类加载器—JVM中运行。这可以通过将以下条目集成到定义了Maven测试运行器的pom.xml中来实现(即,在maven-surefire-plugin中):

B. 使用Maven生成测试

到目前为止讨论的是配置选项处理运行生成的测试的情况,但仍然存在首先生成这些测试的任务。虽然在软件开发过程中从IDE调用软件工程师本地机器上的EVOSUITE可能是一个可行的方案,遗留系统可能不是最好的。在具有数千个类的大型工业软件系统上首次使用EVOSUITE时,在远程专用服务器上运行EVOSUITE更为合理,因为这将是一项非常耗时的任务。为了简化此配置并避免需要准备脚本来相应地调用EVOSUITE,我们实现了一个嵌入式Maven插件EVOSUITE的版本。例如,要为使用64个内核的系统中的所有类生成测试,软件工程师只需键入:

mvn -Dcores=64 evosuite:generate

要获取所有执行目标,可以按如下方式调用EVOSUITE Maven插件:

mvn evosuite:help

或:

mvn evosuite:help -Ddetail=true -Dgoal=generate

获得例如生成目标的参数列表。特别是,可以配置诸如使用的核心数,分配的内存或每个类花费的时间等。

要启用EVOSUITE插件,软件工程师只需将以下插件声明复制并粘贴到根pom.xml文件中:

通过这样做,不再需要进行任何安装或手动配置:Maven将自动处理它。注意:如果插件不在Maven Central Repository4中,则需要添加存储插件的服务器的URL,但这只需要执行一次(例如,使用像Nexus5这样的存储库管理器在企业缓存中)。

一旦通过编辑pom.xml文件(只需要执行一次)来配置EVOSUITE Maven插件,如果工程师想要在新服务器上生成测试,那么只需要在那里上传目标系统(例如,git clone如果Git用作源存储库管理器),然后输入mvn evosuite:generate。这就是使用EVOSUITE的默认配置生成测试所需的全部内容(可以添加一些参数来指定要使用的核心数,运行EVOSUITE的时间长度,如果只测试类的子集,等等)。

IDE集成单元测试生成:

生成的单元测试需要依赖运行时间才能运行,将EVOSUITE嵌入IDE插件中变得更加困难,因为潜在的EVOSUITE版本不匹配:IDE插件可以使用版本X,而项目可能依赖于Y。尝试保持这些版本的一致性并非易事:软件工程师可能同时处理不同的项目,每个项目使用不同的版本;软件工程师在构建中推送新版本更新将破坏所有他/她的同事的IDE插件,他们将被迫手动更新他们的IDE插件。

我们的解决方案是尽可能保持IDE插件的轻量级,并依靠构建本身来生成测试。例如,IDE插件仅用于选择哪些是CUT,以及使用哪些参数。然后,当需要生成测试时,IDE插件只会产生一个调用mvn evosuite:generate的进程。通过这样做,目标项目配置的EVOSUITE版本无关紧要,更新它对用户是透明的。此外,每次发布新版本的EVOSUITE时,都不需要更新IDE插件,只需更新pom.xml文件。

但是,要实现这一点,IDE之间的接口插件和Maven插件需要稳定。这在自动化测试数据生成中并不是真正的问题,通常用户真正感兴趣的参数很少:包括应该为哪些CUT生成测试,使用什么资源(内存,CPU核心和时间)。

这种方法适用于我们的一些工业合作伙伴,但并非适用于所有人:例如,有些人使用Gradle来构建他们的软件而不是Maven。此外,当没有使用构建工具时,依赖构建工具并不起作用,例如,何时直接在IDE中创建新项目。为了解决处理我们没有插件的构建工具的问题,或者根本没有处理没有构建工具的情况,我们还发现有必要选择使用外部命令行EVOSUITE可执行文件,即IDE插件调用单独生成的进程。由于相应的jar文件不需要成为构建的一部分,因此可以直接将其添加到源存储库(例如Git),而无需更改有关系统构建方式的任何内容。这样,同一项目中的所有开发人员都将使用相同的版本,而无需手动下载和配置。

关于生成的测试的运行时依赖性,这对于像Ant / Ivy和Gradle这样的构建工具来说不是问题,因为它们可以使用Maven存储库。但是,如果不使用构建工具,则需要手动添加和配置运行时依赖项(与任何其他第三方依赖项一样)。注意:EVOSUITE可执行文件也可以用作运行时依赖项(它是它的超集),但它会在构建中带来许多新的第三方库。如果项目中已经使用了其中一些库,则可能会导致版本不匹配问题。

连续测试生成:

尽管在开发者机器上按需生成测试是可行的,但是在远程服务器上运行测试生成可能有很多原因。特别是,在源代码更改后反复在许多类上运行EVOSUITE可能很麻烦。为了解决这个问题,我们引入了连续测试生成的概念,其中连续集成(CI)(例如,Jenkins和Bamboo)通过自动测试生成进行了扩展。简而言之,远程服务器将使用EVOSUITE Maven插件在每次新代码提交时运行EVOSUITE。只有改进现有回归套件的新测试才会使用Jenkins CI系统的插件发送给用户。

经验教训

在为Maven,IntelliJ IDEA和Jenkins开发插件时,我们学到了几个重要的课程,我们将在本节中讨论。

在工业中应用测试数据生成技术向我们展示了我们之前没有想到的新问题和背景。如已经存在和新创建的测试的混合执行,我们也开始了解我们之前没有听说过的新工具,例如IntelliJ IDEA和Gradle。在工业中,有大量常用工具,它们塑造了软件开发过程。从学术研究到行业实践的技术转移必须考虑这些工具,以及如何将新的研究技术与它们整合。只有与行业密切合作,并应用实际系统研究所产生的技术,才能实现这一目标。

开发插件通常是一项非常耗时且繁琐的任务,不一定是因为特定的技术挑战,而是由于系统缺乏文档。我们分析的大多数工具都提供了一些关于如何编写插件的教程,但这些是非常基础的。 JavaDocs形式的API文档通常非常稀缺。例如,在撰写本文时,IntelliJ IDEA甚至没有可浏览的JavaDoc文档。学习如何为IntelliJ IDEA开发插件的“推荐”方法是查看其源代码,并研究其他已有的开源插件。在Jenkins插件的开发过程中也发生了同样的事情:虽然有超过1300个Jenkins插件(在撰写本文时)和设置IDE(Eclipse或IntelliJ IDEA)以开发和构建Jenkins插件的文档非常完整的。令我们惊讶的是,每次加载页面时,Jenkins都不会读取几个文件来构建所有Web界面。相反,它在完成构建后序列化构建的所有数据。这当然是加速Jenkins的一个功能,但由于缺少文档,我们需要一段时间来理解它并正确使用它。

插件应该尽可能轻量级,其中大多数功能应该在测试数据生成工具中。插件应仅用于通过一些参数启动测试数据生成,并提供有关生成何时完成的反馈,或在出现错误时发出警告。

Java是一种非常便携的语言。感谢Java,我们已经能够在所有主要操作系统上应用EVOSUITE及其插件,包括Mac OS X,Linux,Solaris和视窗。但是,这并不简单。在学术界,Mac和Linux系统非常普遍。后者尤其如此,因为Linux计算机集群经常用于研究实验。但是,在行业中,Windows系统并不少见,而且在应用EVOSUITE时,最初我们的插件不能用于该操作系统。常见的问题是文件路径的处理, 通过在创建路径变量时使用常量File.separator,可以很容易地在Java中修复此问题。另一个小问题是GUI的可视化,要解决此问题,需要在两个操作系统上打开插件,并执行布局修改,直到弹出对话框在两个系统中都令人满意。

总结

在本文中,我们介绍了我们为EVOSUITE开发的三个插件,使其可以从Maven,IntelliJ IDEA和Jenkins使用。这样做是为了改进EVOSUITE与大型工业软件项目开发过程的集成。我们根据我们在工业合作伙伴中开始应用EVOSUITE的经验,讨论了我们的架构选择的动机,并介绍了技术细节和经验教训。

我们的插件的体系结构并非特定于EVOSUITE,并且原则上可以重复用于其他测试数据生成工具,例如Randoop,jTExpert,GRT和T3i。但是,为此,我们需要将传递给这些工具的输入参数的名称(例如,如何指定要测试的类以及最多可以使用多少个核心)形式化,然后它们需要更新才能使用此信息。

我们还需要进一步的工作来支持其他IDE和构建工具。例如,我们将更新当前的Eclipse插件,使其具有与IntelliJ插件相同的体系结构。此外,随着Gradle在行业中的使用,我们也计划支持它。在本文中,我们介绍了EVOSUITE Jenkins插件的第一个原型版本,虽然它可以使用,但是还有很多潜力:例如,尽管测量了现有测试的覆盖范围,但尚未在覆盖可视化中使用。特别是,详细查看现有测试涵盖所有CUT的哪些部分,新生成的测试涵盖哪些部分以及哪些部分尚未涵盖,将会很有帮助。

EVOSUITE及其插件可免费下载。源代码是在LGPL开源许可下发布的,它托管在GitHub上。有关更多信息,请访问我们的网页:http://www.evosuite.org/

致谢

此文由南京大学软件学院2018级硕士李林昱翻译转述。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接