使用预训练Transformer模型为单元测试用例生成准确的断言声明

互联不一般哥 2024-04-29 10:50:58

Generating Accurate Assert Statements for Unit Test Cases using Pretrained Transformers

Michele Tufano, Dawn Drain, Alexey Svyatkovskiy, Neel Sundaresan

Microsoft

Redmond, WA, USA

Email: {mitufano, dadrain, alsvyatk, neels}@microsoft.com

引用

Tufano M, Drain D, Svyatkovskiy A, et al. Generating accurate assert statements for unit test cases using pretrained transformers[C]//Proceedings of the 3rd ACM/IEEE International Conference on Automation of Software Test. 2022: 54-64.

论文:https://dl.acm.org/doi/abs/10.1145/3524481.3527220

摘要

在本文中,我们提出了一种方法,通过生成准确和有用的断言声明来支持开发人员编写单元测试用例。我们的方法是基于一个最先进的Transformer模型,最初是预先训练的英语文本语料库。然后,在大量的源代码语料库上,以半监督的方式训练这个语义丰富的模型。最后,我们在单元测试生成断言语句的任务上来调整这个模型。所生成的模型能够为被测试的给定方法生成准确的断言语句。

1 引言

软件测试被认为是软件生命周期中最关键、最具挑战性和最昂贵的部分之一。软件测试研究社区在设计旨在支持或自动化软件测试活动的方法上投入了大量的精力。虽然这些工作代表了实现自动化测试目标的显著成就,但它们有一些局限性,最近一些研究中强调了这一点。

这些工具旨在克服的主要挑战之一是生成准确的断言语句,这些语句通常被发现是不完整的或不足以适当地测试软件组件的行为。生成有意义的断言语句是自动测试用例生成中面临的关键挑战之一。断言语句表示软件测试的基本块,被开发人员用于检查程序中的条件或状态,以及关于程序正确性的原因。

Watson等人最近提出了ATLAS,这是一种基于RNN的方法,旨在从数千个单元测试中学习方法如何生成有意义的断言语句。受到这项工作的启发,我们对其进行了实质性的改进。在本文中,我们提出一种方法来生成准确的断言语句,该方法基于最先进的Transformer模型,并且依靠转移学习实现一流的性能,在第一次尝试中就可以预测62%的情况下的正确断言。

迁移学习是一种技术,它首先以无监督的方式对大量无标记数据进行训练模型,然后对下游任务进行分类或翻译。这项技术已经成为自然语言处理(NLP)任务中实现最先进的结果的标准途径,与从头开始训练每个任务相比,能够获得更高的性能和需要更少的资源。对这种成功的直观解释是,一个学习生成文本或填充空白的模型将会产生反映在数据中的偏见,这可以帮助它学习更快、以更高的性能执行特定的语言任务。

在本文中,我们将此想法扩展到源代码作为语言的自动断言语句生成任务中。我们在一个大型源代码和英语语料库上对序列到序列的Transformer模型进行了预训练,并在一个断言语句生成任务上对其进行了微调。

在我们广泛的经验评估中,我们评估了我们的方法的几个属性,例如内在模型度量以及与生成的断言相关的外部度量。最后,我们在一个场景中评估所提出的方法,在该场景中通过使用我们的方法生成的断言语句来增加测试用例来支持自动测试用例生成工具,如EvoSuite。

综上所述,本文提供了以下贡献:

一种基于序列到序列的Transformer模型生成准确的断言语句的方法。在第一次尝试中,我们的方法可以预测62%的情况下的正确断言,当允许模型生成更多的断言时,可以达到高达84%的正确性。我们通过经验证明了为下游断言生成任务预训练英语语料库和源代码语料库的好处,从而在各种内在指标方面提高性能,如BLEU分数、验证损失和语法正确性。我们将研究如何使用我们提出的方法来增强现有的测试用例,例如由EvoSuite生成的测试用例,以及导致测试覆盖率改进的附加断言语句。

2 技术介绍

图1提供了我们在构建专门用于断言语句生成的模型时所遵循的训练管道的概述。我们从最先进的BART模型开始。这将作为我们的模型的参考架构。我们采用了两个预训练阶段:英语预训练,我们对大量的英语文本语料库进行半监督的预训练;代码预训练,其中模型在Java源代码上进行了预训练。接下来,我们对为单元测试用例生成断言语句的任务执行微调,依赖于测试用例和被测试方法的标记数据集。最后,我们评估了用不同水平的预训练获得的模型的变体,如图1中不同的箭头所示。

图1:模型培训过程的概述。从一个BART模型开始,我们执行英语和源代码的预训练,并在断言生成任务上对模型进行微调。根据预训练的水平,我们得到了四种不同的模型。

A.BART模型

BART 是一种去噪自动编码器,它利用了标准序列到序列Transformer架构。我们选择BART模型架构是因为它有助于对断言语句生成的下游翻译任务进行微调,提供了一组更高级的噪声转换,其中包括令牌屏蔽、令牌删除、填充和语句排列。该模型通过破坏文档和优化解码器输出与原始输入序列之间的交叉熵损失来进行预训练。

我们对BART大型模型架构进行了预训练,在编码器和解码器中有12层。模型采用混合精度训练,采用Adam随机优化程序。

B.英语预训练

在这一阶段,我们在大量的英语文本语料库上以半监督的方式对模型进行预训练,目的是学习自然语言的语义和统计特性。

1)数据集:对从书籍、维基百科和新闻文章中提取的160GB英语文本进行了40个周期的预训练,总共包含X行文本。

2)训练策略: BART以无监督的方式进行训练。给定损坏的文本,其目标是重建原始文本。

C.代码预训练

在这一阶段,我们对一个用Java语言编写的源代码语料库的模型进行了预训练,目的是学习源代码的语法和属性。

1)数据集:我们通过在GitHub上爬取至少有50颗星的所有公共的、非分叉的Java存储库来收集这个代码语料库数据集。然后,我们在文件级使用哈希函数进行重复数据删除。

2)训练策略:采用与英语训练前类似的训练前策略。

D.断言微调

在这个阶段,我们将在为单元测试用例生成断言语句的任务上调整一个模型。具体来说,我们将这个任务表示为一个翻译任务,其中源代码是部分编写的单元测试和被测试的方法,而目标是开发人员为该单元测试编写的正确的断言语句。

1)数据集:为了执行微调,我们依赖于用于评估ATLAS的单元测试方法的公开数据集。该数据集由测试方法(即单元测试用例中的方法)、焦点方法(即被测试的方法)和断言(即测试方法中的断言语句)组成。该数据集已从9000多个开源GitHub项目中挖掘出来,这些项目包含用JUnit定义的单元测试用例。

对于我们的模型,我们使用数据集的原始版本——包含原始源代码标记的语料库——而不是抽象版本,因为我们的目标是利用所有变量和方法名的丰富语义。我们将数据集的原始分割保留在训练集、测试集和验证集(80%、10%、10%)中,以进行公平的比较。表1报告了数据集中的实例数。

表1:用于英语和源代码预训练的数据集,并断言微调

2) 训练策略:微调过程是一个翻译任务。在训练期间,我们使用交叉熵损失和Adam优化器,并监测验证集上的损失,以提前停止。我们使用编码器和解码器之间共享的词汇表嵌入来优化,并且因为我们的输入和输出语言是相同的(即Java源代码)。

E. 模型变体

在这些阶段结束时,我们根据所执行的预训练水平,获得了模型的四种不同变体:

BART_Scratch:一个没有在任何语料库上进行预先训练,但在断言生成任务上直接完成的模型。这个模型表示图1中的橙色线。BART_English:一个在英语语料库上进行了预训练,然后完成断言生成任务的模型。这个模型表示图1中的绿线。BART_Code:在源代码语料库上预先训练的模型,然后完成断言生成任务。这个模型表示图1中的紫色线。BART_English+Code:一个模型首先对英语进行预训练,然后进一步对源代码语料库进行预训练,然后完成断言生成任务。这个模型表示图1中的蓝线。

3 实验评估

3.1 实验设置

研究问题。在本文中,我们研究以下研究问题:

RQ1:我们的方法与ATLAS比较如何?

RQ2:预训练过程对断言生成任务有什么影响?

RQ3:焦点法对模型性能的影响有什么?

RQ4:生成的断言的质量如何?

RQ5:我们的方法可以用来改进自动生成的测试用例吗?

RQ1 结果比较

实验设计。我们将我们的模型与ATLAS的性能进行了比较,这是文献中可用的基于RNN的方法。具体来说,为了确保公平的比较,我们在完全相同的训练集上对我们的模型进行微调过程,并且评估和比较在同一测试集上的性能。为了比较模型的性能,我们使用了top-k准确性度量,它度量具有不同预测数量的模型的准确性。

结果。图2报告了我们的四种模型变体和ATLAS的top-k精度。x轴表示k值,范围从1到50,而y轴表示在测试集中正确断言的百分比。对于ATLAS,我们通过考虑最佳模型(即抽象模型)来报告它们在原始工作中出现的结果。

图2:测试断言对的例子(TAP)

结果表明,我们的模型在任何k值下都优于ATLAS。特别是,BART_English+Code变体可以在第一次尝试中的62%的情况下正确地预测目标断言语句(最初是由开发人员编写的)。在实际开发环境中,该方法的可用性和适用性方面的惊人结果尤为重要。实际上,开发人员可以在一个或很少的建议中获得准确和相关的断言声明,而不需要在经过一个长长的列表时丢弃错误的建议。

RQ2 影响分析

实验设计。我们研究了在英语语料库、源代码和两个语料库上对模型进行预训练是否对模型在断言生成任务上的下游性能有明显的影响。特别地,我们将没有在任何语料库上预训练的BART_Scratch模型与在英语语料库上预训练的BART_English、在源代码上预训练的BART_Code和在英语和源代码上预训练的BART_English+Code进行了比较。在执行此比较时,会考虑到:

外部指标:断言生成任务的前k准确性;内在指标: (i)最佳验证损失和达到它所需的训练步骤数(例如,更快的收敛);(ii)BLEU4分数,机器翻译任务的一个常见度量,在测试集上进行评估;(iii)生成的断言的句法正确性,使用Java解析器确定。

结果。外部指标:图2显示了未经过预训练的模型(BART_Scratch)与英语(BART_English)、源代码(BART_Code)和两者(BART_English+Code)预训练模型的性能之间的巨大差距。具体来说,BART_Scratch与单个预训练阶段(BART_Code或BART_English)模型之间的性能差距为22-25%,而BART_English与BART_English+Code之间的性能差距为1.54-2.25%,在英语和源代码上均进行预训练的模型更有优势。

这些结果强调了预训练对下游任务的执行的重要性。在涉及源代码的下游任务中,预训练对自然语言英语文本的影响尤其显著。这个结果强调了有一个能够理解代码中变量和方法名的语义的模型的重要性。

内在指标:在内在指标方面,图3显示了四种模型变化在训练过程中验证集上的交叉熵损失。与使用外部度量观察到的类似,我们注意到没有预训练的模型(BART_Scratch)与有英语(BART_English)、源代码(BART_Code)和(BART_English+Code)预训练的两个模型之间有很大的差距。比较仅English和English+Code模型,对源代码的额外预训练有三个明显的效果: (i)较低的初始损失(0.21 vs 0.31);(ii)较低的最佳损失(0.13 vs 0.15);(iii)收敛更快。

图3:在对我们的四个BART模型变体的断言微调过程中获得的验证损失

表2报告了为四种模型变化计算的内在度量。具体来说,BART_English+Code获得最好的BLEU4和验证损失。关于句法正确性,在英语和源代码预先训练的模型获得最好的价值预测,然而当在计算正确性时,并且为每个测试集的输入仅考虑前25,和50的生成断言,模型预先训练只在源代码达到最高的正确性得分。这个结果在某种程度上是可预测的,因为BART_Code已经专门对源代码进行了预训练和微调,因此它在语法方面应该具有最一致的结果。

总的来说,我们观察到预训练对英语和源代码的外在指标和内在指标都有显著的积极影响。虽然对源代码的额外预训练似乎比单独的英语预训练的影响更小,但值得注意的是,我们在所有分析的指标中观察到一致的改进,证实了源代码预训练的有益效果。此外,这个小的差距可能是由于下游任务的性质,其中的输出是一个单行断言语句,它可能非常类似于一个自然语言的句子。

表2:准确的预测

RQ3 影响分析

实验设计。在这个研究问题中,我们的目标是理解在生成断言语句时,作为模型输入的焦点方法对性能的影响。特别地,我们选择了从RQ1和RQ2中获得的最佳模型,并与等效模型(即相同的预处理步骤)进行比较,但在不包含焦点方法的并行语料库上进行细化。具体来说,这可以看作是一个自动完成任务,我们比较了有或没有焦点方法的模型在测试集上的top-k精度方面的性能。

结果。为了回答这个研究问题,我们将在RQ1和RQ2中取得最佳性能的模型BART_English+Code与相似的模型(即相同的预训练阶段)进行了比较,但具有不同的微调。具体来说,我们在源代码预训练后选择了相同的模型检查点,并在没有焦点方法作为输入的情况下,对修改后的数据集上的模型进行了微调。图4显示了使用(实线)和没有(虚线)焦点方法的模型的top-k精度。结果表明,以焦点方法作为输入的模型在约10%的案例中生成断言语句更准确。也就是说,即使产生了50种不同的预测,该模型也没有包含某些断言声明。这个结果突出了焦点方法提供的基本信息,以告知模型生成特定的断言语句。

图4:top-k精度-比较我们的模型训练有或没有焦点方法作为输入

RQ4 性能结果

实验设计。在最后一个研究问题中,我们调查了由模型生成的断言陈述的质量。我们手动分析正确预测的实例,并检查那些与目标断言声明不匹配的实例。我们报告了定性的例子和讨论。

结果。为了回答这个研究问题,我们分析和讨论了生成的断言语句的例子。图5提供了由我们的最佳模型BART_English+Code生成的常见、复杂和等价的断言语句的例子。公共断言的列表包括由模式正确预测的语句(即,匹配原始断言),这些语句通常在不同上下文中的测试用例中找到。

图5:生成断言的示例

最后,图6报告了两个完整的源和目标的例子,由模型正确预测。在第一个示例中,生成的断言将检查使用事件工厂创建的事件对象是否属于正确的类实例。在第二个示例中,该模型生成了一个复杂的断言语句,其中包含数字文字和以前用于设置测试环境的变量。此外,图2中描述的示例该模型在第一次尝试中也正确地预测了。

图6:两个完美预测的完整例子

这些结果强调了除了简单的准确性之外,还需要额外的指标。特别是,能够识别生成的断言语句与人类开发人员创建的语句不同但等价的情况,以及在该上下文中也正确的非等价断言。此外,在代码中可能有许多位置,开发人员没有引入断言语句,但模型可以建议合理的语句,这些位置目前在定量度量中没有发现。这个研究问题的主要目标恰恰是通过定性和手工分析来填补这一空白。

RQ5 扩展分析

实验设计。本研究问题的目的是提供一个关于使用我们的方法来改进自动化测试用例生成工具的潜在好处的初步调查,比如EvoSuite。具体来说,我们的目标是通过插入由我们的方法生成的额外断言来增强由EvoSuite生成的测试用例。我们评估了在测试覆盖率提高方面的潜在好处,并定性地讨论了额外的断言。

在本研究中,我们选择了一个小但可重复的试验台,使用defects4j。我们依赖于defects4j,因为它提供了一个可靠的基础设施来生成、编译、执行和评估测试用例。我们让EvoSuite生成500秒(约8分钟)的测试用例,并使用defects4j计算测试覆盖率,特别于每个测试用例。

接下来,我们选择了该类的18个唯一的焦点方法,而不考虑方法的重载副本,以及由EvoSuite生成的相应测试用例。我们为每种焦点方法选择了一个最佳的测试用例。一旦我们有了映射的测试用例对,我们就会使用我们的方法为每个对生成额外的断言语句。具体来说,对于每个焦点方法,我们生成前10个预测,并从它们中选择一个断言,我们将其作为EvoSuite测试用例中的最后一个语句插入。最后,我们执行新扩增的测试用例,并重新计算每个用例的测试覆盖率。

结果。表3报告了实验中考虑的18种公共方法中的每一种在类水平上的绝对(和百分比)线和条件覆盖率。表3显示了原始EvoSuite测试用例的结果,我们的模型增强的结果,以及最后一列的增量改进。

表3:试验覆盖范围分析

对于18种方法中的13种,我们的方法生成了断言,对于4种方法,我们的方法生成了正确的断言,但没有提高覆盖率,而对于1种方法,我们的方法在前10个预测中没有生成任何正确的断言。

图7显示了用于增强EvoSuite测试用例的所有生成的断言,其顺序与表3中报告的方法相同。我们可以注意到,前三个断言使用实际数值调用焦点方法,这将导致额外的测试覆盖率。现在让我们关注四个没有提高覆盖率的断言声明,对应于焦点方法min和max。其中三个断言只是简单地对EvoSuite使用的返回变量执行额外的检查,即长变量0、浮点数0、字节0。这些断言不调用焦点方法,因此不会导致额外的覆盖,而是关注测试重新值的附加属性。

图7:由我们的方法为Lang-1-f生成的断言语句

总的来说,这些结果表明,我们的方法可以帮助使用额外准确的断言语句增强现有或自动生成的测试用例。在我们实验中报告的大多数案例中,我们发现这些断言略微提高了测试覆盖率。

转述:张雅欣

0 阅读:0

互联不一般哥

简介:感谢大家的关注