基于大语言模型的自动化单元测试生成系统及测试套件评估方法
A System for Automated Unit Test Generation Using Large Language Models and Assessment of Generated Test Suites
翻译于上述论文
基于大语言模型的自动化单元测试生成系统及测试套件评估方法
摘要
单元测试是软件测试生命周期中最基础的测试层级,对确保软件正确性至关重要。设计和创建单元测试是一个成本高昂且劳动密集型的过程,非常适合进行自动化。近年来,大语言模型(LLMs)已被应用于软件开发的各个方面,包括单元测试生成。尽管已有若干评估LLMs在测试代码生成能力的实证研究,但这些研究主要关注简单场景,例如为单个方法直接生成单元测试。这些评估通常涉及独立且小规模的测试单元,未能全面反映LLMs在真实软件开发场景中的表现。此外,先前的研究未能以适合实际应用的规模处理问题。生成的单元测试通常通过手动集成到原始项目中进行评估,这一过程限制了执行的测试数量并降低了整体效率。
为解决这些不足,我们开发了一种生成和评估更接近真实复杂度的测试套件的方法。我们的方法专注于类级别的测试代码生成,并自动化从测试生成到测试评估的整个过程。本文中,我们介绍了AgonetTest:一个为Java项目生成测试套件的自动化系统,以及一套全面且系统的评估生成测试套件的方法。基于最先进的数据集(即Methods2Test),我们构建了一个新的数据集,用于比较人工编写的测试与LLMs生成的测试。我们的主要贡献包括一个可扩展的自动化软件系统、一个新数据集以及详细的测试质量评估方法。
关键词:软件测试、大语言模型、自动化评估
I. 引言
软件测试是软件开发生命周期中的关键步骤,对确保代码正确性和可靠性至关重要。其中,单元测试是验证单个代码单元功能是否正常的阶段。设计和构建单元测试是一个成本高昂且劳动密集型的过程,需要大量时间和专业技能。自动化这一过程是一个充满前景的研究和开发领域。
自动生成单元测试的工具可以减轻测试工程师和软件开发人员的工作负担。这些工具通常使用静态代码分析方法生成测试套件。例如,EvoSuite [1]是一种结合静态代码分析和进化搜索的流行工具,已被证明能够实现足够的覆盖率。
大语言模型(LLMs)已高效应用于软件开发的多个方面,同样可以处理单元测试的自动生成。多项关于LLMs的实证研究强调了它们在简单场景中生成测试的能力,通常仅限于单个方法[2, 3, 4, 5]。尽管这些探索具有一定的方向性意义,但它们关注的是独立且小规模的测试单元,未能全面反映LLMs在真实软件开发场景中的表现[6]。此外,先前的研究未能以适合实际应用的规模处理问题。生成的单元测试通常通过手动集成到原始项目中进行评估,这一过程限制了执行的测试数量并降低了整体效率。
为解决这些不足,我们开发了一种生成和评估更接近真实复杂度的测试套件的方法。我们的方法专注于类级别的测试代码生成,并自动化从测试生成到测试评估的整个过程。
本文中,我们介绍了AgonetTest,这是一个为Java项目生成测试套件的自动化系统,并附带一套严格且系统的方法来评估这些生成的测试套件。基于Methods2Test数据集[7],我们开发了一个新的数据集,专门用于比较人工编写的测试与LLMs生成的测试。我们集成了JaCoCo、PITest和TsDetect等库来计算测试评估指标。
我们的工作主要贡献如下:
- AgoneTest:我们设计并开发了一个闭环、高度自动化的软件系统,支持大规模生成和评估单元测试的过程。该系统的初始版本针对真实开源Java项目,集成了JaCoCo、PITest和TsDetect等关键库;
- 方法论:基于AgoneTest,我们提出了一套全面评估多种LLMs及其提示技术和提示模式的方法,以及用于评估生成测试套件质量的指标和测试坏味道;
- Classes2Test:一个扩展Methods2Test[7]的带注释的开源Java项目数据集,将目标类与其相关的测试类映射起来。这一扩展数据集使得评估LLMs在整个类上的测试表现成为可能,而不仅限于单个方法。
本文的组织结构如下:第二节介绍背景并强调我们的工作与相关工作的差异;第三节概述AgoneTest及其模块,详细说明其功能范围;第四节通过一个端到端的示例展示AgoneTest的实际应用;第五节通过框架的初步评估回答关键研究问题;第六节总结实验中的见解和经验教训;第七节讨论我们方法的局限性;第八节总结全文并展望未来工作方向。
II. 背景与相关工作
A. 单元测试生成
单元测试生成是为单个软件组件(如函数、方法或模块)自动创建测试用例的过程。这些测试用例用于独立验证每个单元的功能是否正确。
现有技术采用基于随机性[8, 9]、基于约束[10, 11]或基于搜索的方法[12, 13]。这些方法的核心思想是将问题转化为可通过数学方法解决的问题。例如,基于搜索的技术将测试转化为优化问题以生成单元测试用例[14]。因此,这些技术的目标是生成所有可能的解决方案,然后选择那些能够实现更高代码覆盖率的方案。EvoSuite [1]通过接受Java类或方法作为输入,并应用基于搜索的算法生成满足代码或分支覆盖率等标准的测试套件。EvoSuite通过变异、选择和优化的迭代过程评估测试的适应性。它不仅生成JUnit测试用例,还通过检查生成的测试套件的效率(基于代码覆盖率和变异分数等指标)提供全面的报告。EvoSuite的一个局限性是其生成的测试通常缺乏清晰性和可读性[15]。此外,EvoSuite仅适用于使用Java 9或更低版本的项目,这限制了其在更现代Java项目中的适用性(目前最新的Java版本为22)。与EvoSuite不同,AgoneTest通过集成先进的评估指标和测试坏味道识别,提供了对生成测试套件质量的更全面评估,并利用类似人类编写的LLM生成代码确保可读性。此外,AgoneTest支持所有Java LTS版本,使得基于更新版本构建的项目也能被测试,克服了EvoSuite的兼容性限制。
B. 用于测试生成的大语言模型
自LLMs出现以来,它们已被用于测试套件生成。最初利用LLMs的技术被视为神经机器翻译问题的解决方案[16, 17]。这类方法通过从主要方法翻译到适当的测试前缀或测试断言,同时使用测试生成数据集对LLMs进行微调。例如,AthenaTest [17]使用测试生成数据集优化BART [18],其中源是主要方法及其对应的代码上下文,结果是完整的测试用例。AthenaTest主要专注于通过微调单个模型生成方法级测试,而AgoneTest将重点转向类级测试的生成。我们的方法使得可以使用最新的LLMs,并且不受提示设计的限制,从而能够处理更复杂的真实场景。
随着指令调优LLMs的快速发展,利用适当提示引导LLMs生成测试的方法正在兴起,而不是通过模型微调[19, 20]。目前已出现多项评估LLMs在测试套件生成中的能力的提案。例如,ChatTester[5]提出了一种基于ChatGPT评估和改进LLM生成测试的工具。ChatTester专注于改进和评估特定LLM(ChatGPT)生成的测试,但需要人工干预来评估生成的代码,并且未提供对多种LLMs在类级测试上的评估。AgoneTest则支持多种LLMs,并在大量真实Java项目上评估每种LLM的表现。TestPilot[3]也专注于使用LLMs生成和改进JavaScript代码的测试。尽管TestPilot进行了自动化评估,但其适用性仅限于其参考工作中提到的25个仓库。AgoneTest通过使用包含9,410个Github仓库的数据集,并自动集成测试库,提供了更广泛的适用性。Cedar[21]提出了一种基于少样本学习[22]和Codex模型的提示构建策略来生成测试。Cedar使用特定的提示构建策略,但未集成结构化机制以统一框架评估多种LLMs和提示技术。AgoneTest通过允许集成和评估各种提示工程技术和LLMs,提供了更全面的测试生成方法。Guilherme和Vincenzi [2]使用gpt-3.5-turbo分析模型超参数变化的影响。Guilherme和Vincenzi的研究提供了初步评估,但缺乏在变异覆盖率和测试坏味道等全面测试质量指标上的自动化评估。AgoneTest通过自动化这些评估,集成先进指标以提供对生成测试的更深入分析,进一步推进了研究。Siddiq等人[4]提出了一种使用常见数据集评估生成测试的新方法,并尝试使用新指标[23]。尽管Siddiq等人使用了测试正确性(但未使用变异覆盖率)以及AgoneTest使用的所有指标,但其方法未完全自动化测试生成-执行-评估循环,也未专注于类级测试。AgoneTest通过提供端到端自动化并专注于生成和评估复杂的类级测试套件,填补了这一空白。
C. 当前LLMs应用于单元测试生成方法的局限性
尽管前景广阔,当前将LLMs应用于单元测试生成的方法仍存在若干局限性:
a) 范围有限:当前评估LLMs在测试代码生成中有用性的方法大多仅限于生成代码片段,而非整个模块或组件(例如Java中的整个类)。因此,研究社区缺乏用于评估类级测试生成的专用数据集。据我们所知,研究通常仅对生成结果提供零散和轶事性的评估[3, 5, 17]。
b) 缺乏自动化:目前尚未有文献提出能够完全自动化测试生成-执行-评估循环的工作,而这对于全面和可扩展的测试至关重要[3, 19, 20]。
c) 提示选择的主观性:在大多数情况下,选择提示以让LLMs生成测试代码仍然是主观的。没有对最初提出的提示技术与其他替代技术进行彻底评估,这为提示工程的进一步探索和优化留下了空间[4, 20, 21]。
III. AGONETEST概述
“agone”一词源于古希腊和罗马,指的是哲学家们辩论观点并由观众决定胜负的竞赛。我们借用这一术语隐喻性地表示LLMs及其各自提示策略在生成最优单元测试套件的竞技场中的竞争性评估。AGONETEST基于标准测试质量指标(我们将在后续章节中详细说明)确定最优策略。
AGONETEST旨在为软件测试人员提供一个生成和评估单元测试的系统。这一评估侧重于代码覆盖率和已知测试坏味道等关键指标,从而提供对测试套件质量的全面评估。
AGONETEST基于以下原则运行:通过测试工程师和数据科学家(或提示工程师)的协作,可以评估LLMs在生成高质量单元测试任务中的表现。然而,在实践中,一位熟悉生成式AI的经验丰富的测试工程师可以同时扮演这两个角色,从而只需专注于定义新的提示技术和LLMs的比较。在本文的剩余部分中,当我们提到AGONETEST用户(或“测试工程师”)时,指的就是这一角色。
该系统通过以下阶段协助测试工程师:
- 策略配置
- 自动化测试生成
- 策略评估
图1展示了AGONETEST架构的高层示意图,显示了简化测试生成和评估流程的操作模块。这些模块描述如下:
样本项目选择(策略配置 - I):作为初始配置步骤,用户选择要为哪些仓库生成测试套件。这一初始阶段利用我们贡献给社区的一个带注释的开源Java仓库的全面数据集。它涉及准备、加载和管理系统将要测试的仓库。
配置参数提取(策略配置 - II):在这一阶段,从选定的仓库中提取配置参数(例如项目Java版本、使用的测试框架等),并处理这些参数以创建LLMs的提示模板。
提示创建(自动化测试生成 - I):在此阶段,前一阶段的提示模板被完全实例化,然后用于在下一步生成单元测试套件。
测试套件生成(自动化测试生成 - II):在此阶段,AGONETEST协调与选定LLMs的交互,向其提供实例化的提示以生成单元测试代码。每个LLM生成的测试类随后被集成到项目结构中。
A. 配置参数提取
在单元测试生成开始之前,系统从上一阶段选定的项目中提取一些参数。这些参数随后被输入到选择提示和LLMs的模块中。为了查询被检查的模型,可以选择多种提示技术[24]。
配置参数包括:
- focal_class:该变量包含需要为其生成测试套件的Java类;
- testing_framework:该变量提供项目测试框架的名称和版本(例如JUnit 4),在执行期间直接从项目中提取;
- java_version:该变量允许检索项目使用的Java版本;
- example_focal_class & example_test_class:这些变量包含从参考仓库中提取的示例目标类及其对应的测试类,如果用户希望使用少样本提示技术,这些示例对LLM非常有用;
- example_testing_framework & example_java_version:这些变量提供关于示例仓库的信息。
参见第IV-A节中的真实实现示例。
B. 提示创建
在此阶段,前一阶段描述的提示模板被完全实例化,以创建可行的提示来指导LLM生成单元测试。我们通过替换第III-B节中概述的变量来填充用户提供的提示结构。
需要注意的是,为了确保我们的实验和发现可复现,我们通过保存用作源的仓库的提交哈希来准备Classes2Test。这使得AGONETEST能够一致地提取诸如使用的Java版本、测试框架类型(例如jUnit)及其版本等信息。
与之前需要人工输入上下文信息以创建单元测试的LLMs方法不同[2, 5],AgoneTest在更大程度上自动化了这一过程。AgoneTest使用ElementTree [25]和解析器来读取和修改Maven和Gradle构建(参见第III-E3节)。它分析每个构建系统中存在的库和使用的Java版本。这种方法以及使用示例的能力,为用户提供了一个生成提示的灵活系统。
C. 测试套件生成
在流程的这一阶段,我们已经具备了为项目的每个目标类生成测试套件所需的一切。为了确保每个模型具有适当数量的令牌,我们使用tiktoken5(一种BPE分词器[26])来评估提示中的令牌计数。如果超出限制,AgoneTest会向用户返回错误,指明超出的令牌数量。
我们强调,AgoneTest允许用户自动评估多种LLMs。这一能力由开源的LiteLLM库提供,该库基于OpenAI API格式的标准交互,促进了与100多个模型的通信。LiteLLM通过将输入转换为满足每个提供商的独特端点需求,简化了集成。在当前环境中,由于LLM提供商缺乏标准API规范,将多个LLMs集成到项目中具有挑战性,因此这一点至关重要。
在调用LLM之后,AgoneTest从LLM的答案(即生成的测试类)中选择相关信息。这一步骤对整个流程的自动化至关重要,因为LLMs可能会提供详细的描述或解释代码应该如何结构化,而实际上并未生成代码[27]。在此组件中,AgoneTest移除不必要的部分(如大纲描述),并创建一个新文件以将测试类集成到项目中。
D. 测试套件评估
在此阶段,我们根据以下质量指标和测试坏味道评估测试套件的质量。指标和测试坏味道的实际确定通过库集成完成,从而实现测试套件评估的完全自动化。需要注意的是,此组件与后文讨论的实验评估是分开的。相反,它作为AgoneTest提供的额外工具,帮助工程师评估生成测试的质量。
1. 覆盖率指标
- 行覆盖率[28]:该指标衡量测试过程中执行的代码行百分比。100%的行覆盖率意味着软件中的每一行代码在测试期间至少运行了一次。我们选择它是因为它直接显示了被测试的源代码部分。
- 方法覆盖率[28]:类似于行覆盖率,该指标关注代码中的特定方法或函数。100%的方法覆盖率意味着所有方法在测试期间至少运行了一次。该指标有助于识别可能未被充分测试的方法。
- 分支覆盖率[28]:该指标计算测试中执行的决策点(如if或switch语句)的百分比。它确保测试了代码中的所有可能路径,这可以发现仅靠行或方法覆盖率可能遗漏的缺陷。
- 指令覆盖率:该指标计算测试期间执行的Java字节码指令数量。这是一个详细的指标,不受源代码格式的影响,即使类文件中没有调试信息也可以确定。这有助于精确定位未被测试覆盖的最小代码片段。
- 变异覆盖率[28]:该指标评估测试在识别代码中故意引入的变化(变异,如修改算术操作或反转条件)方面的有效性。如果测试检测到所有变异(即识别所有变化),则变异覆盖率得分为100%。选择该指标是因为它衡量了测试套件的鲁棒性。
2. 测试坏味道[23]
这些是低效或问题模式的指标,可能对测试代码的可维护性和有效性产生负面影响。识别测试坏味道有助于随时间提高测试代码的质量,并提高对测试设计中潜在问题的认识。AgoneTest确定代码中是否存在以下测试坏味道:
- Assertion Roulette (AR) [29]:表示包含多个断言语句但无解释/消息(断言方法中的参数)的测试方法数量;
- Conditional Test Logic (CTL) [30]:表示包含一个或多个控制语句(即if、switch、条件表达式、for、foreach和while语句)的测试方法数量;
- Constructor Initialization (CI) [31]:表示测试类是否包含构造函数声明;
- Default Test:表示测试类是否命名为’ExampleUnitTest’或’ExampleInstrumentedTest’;
- Duplicate Assert (DA) [31]:表示包含多个具有相同参数的断言语句的测试方法数量;
- Eager Test (EA) [29]:表示包含对多个生产方法的多次调用的测试方法数量;
- Empty Test (EM) [31]:表示不包含任何可执行语句的测试方法数量;
- Exception Handling (EH) [31]:表示包含throw语句或catch子句的测试方法数量;
- General Fixture:如果测试类的setUp方法中实例化的字段未被同一测试类中的所有测试方法全部使用,则为1;
- Ignored Test (IT) [31]:表示包含@Ignore注解的测试方法数量;
- Lazy Test (LT) [29]:表示调用同一生产方法的测试方法数量;
- Magic Number Test (MNT) [30]:表示包含作为参数的数值字面量的测试方法数量;
- Mystery Guest:表示包含文件或数据库类对象实例的测试方法数量;
- Redundant Print (RP) [31]:表示调用System类的print、println、printf或write方法的测试方法数量;
- Redundant Assertion (RA) [31]:表示包含预期参数和实际参数相同的断言语句的测试方法数量;
- Resource Optimism (RO) [31]:表示使用File类实例但未调用对象的exists()、isFile()或notExists()方法的测试方法数量;
- Sensitive Equality (SE) [29]:表示调用对象toString()方法的测试方法数量;
- Sleepy Test:表示调用Thread.sleep()方法的测试方法数量;
- Unknown Test (UT) [31]:表示不包含任何断言语句和@Test(expected)注解参数的测试方法数量。
3. 库集成
我们使用以下库来计算指标:
- JaCoCo:JaCoCo是一个免费的Java库,用于测量测试套件执行中的代码覆盖率。它帮助开发人员识别代码库中哪些部分已被充分测试,哪些部分尚未被测试,从而更好地理解项目中的测试覆盖率。我们选择JaCoCo是因为其广泛采用、与构建工具轻松集成的能力以及生成报告的功能,这对指标评估至关重要。
- PiTest [32]:PiTest是一个用于Java和基于JVM系统的变异测试系统。它超越了传统的行和语句覆盖率指标,提供了关于测试套件鲁棒性的更具体见解。PiTest在源代码中引入小的变化或变异,然后重新运行测试以确定这些变化是否被检测到。我们选择PiTest是因为与传统的覆盖率工具相比,它提供了对被测系统实际行为和响应的更细粒度和更真实的视图。
- TSDETECT [33]:TSDETECT是一个专注于自动检测软件项目中测试坏味道的库。测试坏味道指的是测试代码中的模式,可能表明设计或实现问题,导致测试可维护性降低并可能阻碍代码理解。选择TSDETECT是因为其能够识别这些坏味道并提供代码改进的可操作指南。
在此阶段,AGONETEST自动将这些库包含到项目中。对于每次运行,AGONETEST检查支持的构建系统(Maven和Gradle,第III-C节)的配置文件,以确定必要的库是否已存在。如果不存在,它会修改配置以添加所需的依赖项。
AGONETEST展示了高度的自动化,如其处理PiTest库的方式所示。具体来说,如果仓库使用JUnit 5测试框架,则需要额外的库“pitest-junit5-plugin”。利用从仓库的提示创建模块(第III-C节)中提取的信息,AGONETEST自动识别使用的测试框架并添加此依赖项,无需任何人工干预。
4. 自动化测试套件评估
添加必要的库后,AGONETEST运行构建和测试以确保没有编译错误。我们流程的测试套件评估阶段具有高度的自动化,如下所述。
AGONETEST生成一份报告,包含为LLM生成的测试计算的测试坏味道和指标结果。为此,该工具自动从库生成的报告中检索详细信息,为每个项目中的每个类编译这些数据。
这一广泛的计算过程使得对生成的测试套件进行详细分析成为可能。通过对比结果,该模块有助于识别与每个LLM和提示配置相关的具体优势和劣势。它提供了关于LLMs表现出色的领域的见解,并突出了可能需要改进的潜在差距。
此外,这种比较有助于清晰理解不同LLMs和提示如何影响测试生成质量的细微差别。它支持识别生成高质量测试的最佳配置。这一详细分析对于改进LLMs和增强其在自动化测试生成中的能力至关重要。
通过提供如此深入的评估,AGONETEST成为研究人员和开发人员的宝贵工具。它有助于LLMs的持续改进,并推动自动化测试领域的进步。最终,它可以确保生成的测试稳健可靠,提高自动化测试解决方案的有效性。
IV. AGONETEST实践
在本节中,我们将通过描述一个实际示例的端到端运行来展示AGONETEST的实际操作。
我们将在描述中跳过仓库选择阶段,直接进入配置阶段,该阶段涉及LLM选择和提示规范。然后,我们将举例说明结果如何呈现给用户以供进一步分析。
A. 配置
如第III-B节所述,AgoneTest使用YAML文件作为输入,可以在其中指定与两个元素相关的信息:llms和prompts。清单1中表示的YAML文件声明了使用OpenAI提供的’gpt-4’和’gpt-3.5 turbo’模型。
清单1. YAML配置文件的设置:为两个不同的LLMs和两个不同的提示设置变量。
llms:- model: gpt-4-1106-previewtemperature: 0- model: gpt-3.5-turbotemperature: 0prompts:- name: zero-shotvalue:- role: systemcontent: You are provided with Java class. Create a test class that fully tests the proposed Java class using the project information for imports. Reply with code only, do not add other text that is not code- role: usercontent: "The project uses {{testing_framework}} and Java {{java_version}} and Java class is : \n<code>\n{{focal_class}}\n</code>"- name: few-shotvalue:- role: systemcontent: You are provided with an example with a Java class and its test class. You are then provided with a new Java class. Take a cue from the example and create a test class that fully tests the new proposed Java class. Reply with code only, do not add other text that is not code- role: usercontent: "#Example:\nThe example Java class is:\n<code>\n{{example_java_class}}\n</code>\nThe example test class is:\n<code>\n{{example_test_class}}\n</code>\nThe Java class you must create the test for is:\n<code>\n{{focal_class}}\n</code>"
提示规范声明包含两个部分:name和value。name是用于标记提示类型的标识符(零样本、少样本等)。而value是一个OpenAI消息类型的数组。每条消息包含一个role和一个content。
role可以是以下类型之一:
- system:用于指示模型应采纳的行为。
- user:用于指示生成测试类的请求。
在YAML配置文件中,指定了两种类型的提示:zero-shot和few-shot。
1. 零样本(Zero-shot)
零样本指的是向模型呈现单个请求或任务实例,而不提供任何先前的示例供模型参考[34]。这种方法强调模型理解和准确执行给定任务的能力。
2. 少样本(Few-shot)
与零样本提示不同,少样本提示涉及向模型提供展示预期输入和输出的示例[22]。这种技术通过将示例包含在提示中,帮助模型进行上下文学习,从而引导模型生成更准确和相关的结果。这些示例作为实际请求的条件,帮助模型生成更准确和相关的结果。
此配置文件将指示AGONETEST执行第III-C节中描述的步骤:它将完全实例化模板变量,包括目标类、仓库使用的测试框架(及其版本)以及仓库中使用的JDK版本。
B. 结果呈现
生成阶段运行完成后,AGONETEST生成一个CSV文件,其中包含为每个选定的LLM和每种提示技术计算的目标类指标以及关于测试坏味道的结果。作为示例,表II显示了该文件的摘录,其中还包含人工编写测试的结果(如它们在CLASSES2TEST数据集中存在的那样)。
通过检查此文件,用户可以深入了解每个LLM和提示组合的优势和劣势。此外,软件测试人员可以准确评估LLM在创建可用和有效的类级测试方面的有效性。这一点在以下部分中变得清晰,我们描述了用于验证AGONETEST的实验设置并讨论了一些结果。
V. 评估
在此实验评估中,我们旨在解决以下研究问题:
- RQ1:在多大程度上可以实现生成测试套件的端到端自动化流程?我们分析框架的自动化程度以及需要人工干预的点(如果有的话)。
- RQ2:是否可以有效评估由不同LLMs和提示策略自动生成的测试套件的质量?我们调查框架是否可以提供关于测试套件在效率和鲁棒性方面的质量信息,并帮助识别优势、劣势和潜在的改进点。
A. 数据集
在我们的实验中,我们从数据集CLASSES2TEST中随机选择了10个仓库。这些仓库总共包含94个目标类,具有不同的长度和复杂性,如表III所示。随机选择的仓库样本大小足以代表真实项目中遇到的变异性(通常由一个或少数几个相互依赖的仓库组成),同时确保我们的系统在规模上是可处理的。
B. LLMs和提示配置
在我们的实验中,我们从LiteLLM支持的模型中选择了两款LLMs。我们选择了’gpt-4’和’gpt-3.5 turbo’模型。选择’gpt-4’是因为其在HumanEval基准测试[35]中的出色表现,而’gpt-3.5 turbo’作为早期一代模型,允许进行有意义的比较。
如表III所示,我们设置了温度参数为0,以增加文本生成的一致性水平(并降低随机性水平),并使生成的不同测试套件具有可比性。关于提示类型,我们决定尝试两种最流行的技术:零样本和少样本。这使我们能够展示AGONETEST的灵活性,并进一步探索第III-B节中看到的上下文变量的使用。在我们的实验中,少样本提示中提供的示例在所有LLMs中始终相同,以确保一致性。该示例包括从一个开放样本仓库中提取的目标类和测试类。
C. 数据收集和分析
并非从我们的样本仓库生成的所有测试套件都是有效的(即测试未通过,在某些情况下,代码甚至无法编译)。类似于[2],我们遇到了一些问题:一些测试由于语法错误或不正确或不存在的导入而无法构建。为了进入评估阶段,系统会自动移除存在错误的测试类以继续编译。一旦所有错误被移除,系统会进行编译和运行测试,以消除所有非绿色套件(即并非所有方法都执行无失败的类)。
这样做有两个原因:首先,在执行根本原因分析后,我们发现失败的测试类之所以失败,是因为它们被错误地指定,而不是因为存在错误。例如,测试失败是因为它们调用了库的私有或过时的方法,或者因为它们调用了不存在的API。第二个原因是拥有绿色套件是PiTest计算变异覆盖率的必要条件。在完成此清理阶段后,系统执行最终编译,运行测试和库以收集评估数据。
表IV显示了实验中编译并通过所有测试的类的数量。平均而言,我们在实验中观察到:
- 75%的生成测试类成功编译;
- 34%的生成测试类是绿色套件并具有可计算的变异覆盖率。
表V显示了不同LLMs和提示技术组合相对于AGONETEST计算的指标的性能的比较分析。我们还报告了人工编写测试的指标,以作为基准。
VI. 讨论与经验教训
在本节中,我们将回答先前定义的研究问题,并讨论经验教训以及未来可能的研究方向。
RQ1:在多大程度上可以实现生成测试套件的端到端自动化流程?
AGONETEST提供了一个端到端的自动化流程来生成和评估测试套件,无需人工干预。然而,我们的实验强调了两个需要注意的点:
- 生成测试类的编译成功率显示还有改进空间(在我们的实验中介于64%和76%之间);
- 测试通过率相对较低(介于30%和38%之间)。
进一步分析显示,许多生成的测试由于不正确的导入或语法错误而失败,而不是因为它们发现了之前未检测到的错误。为了改进这些结果并提高这些百分比,可以探索不同的路径。一种是人工介入:人工干预可能包括手动修复代码错误、调整设置或安装成功执行所需的库。
另一方面,就自动化而言,一个良好的结果是从项目配置文件(如Maven或Gradle)中自动提取上下文信息。这最大限度地减少了人工干预的需求,并提高了生成的提示和测试的准确性。
RQ2:是否可以有效评估由不同LLMs和提示策略自动生成的测试套件的质量?
AgoneTest提供了关于生成的测试套件质量的相关信息,包括代码覆盖率、测试套件对人工注入错误的鲁棒性(即变异覆盖率)以及测试坏味道。实际上,测试坏味道的存在表明测试设计和可维护性可能存在潜在问题。
在表V中,我们可以看到LLMs编写的测试套件在覆盖率方面已经具有良好的质量,但在鲁棒性方面应有所改进。人工编写测试的基准在变异覆盖率方面始终显示出更好的结果。将我们的LLMs驱动系统的输出与实际测试工程师编写的测试进行比较,为我们提供了关于每个模型-设置对的能力和限制的宝贵见解。我们的实验(表V)表明,不同LLMs的性能根据所使用的提示技术而有显著差异。令人惊讶的是,我们发现对于’gpt-4’,零样本提示的结果优于少样本提示。然而,需要注意的是,这并不意味着’gpt-4’是所有场景的最佳模型,这也不是我们实验的目标(即找到最佳模型)。由于模型的性能可能根据上下文的具体情况或提示的结构而有显著差异。因此,我们创建了AgoneTest:为用户提供一个系统,让他们可以尝试各种组合,以找到最适合其特定需求的LLM和提示配置。
A. 经验教训
在AgoneTest的开发和评估过程中,我们收集了几个关键见解,这些见解将指导框架的未来改进。这些经验教训对于完善系统和提高LLM生成测试的有效性至关重要。每个小节下面突出了遇到的具体挑战,并概述了潜在的解决方案。
1. 编译和测试通过率
我们的实验显示,编译和测试通过率在追求完全自动化的过程中还有提升空间。这些问题的原因多种多样(例如,导入的类不存在或缺失)。自动化纠正这些重复出现的问题是可能的[36],并将提高生成测试的成功率。一种有前景的方法是要求LLM本身分析生成的测试代码中的错误并提供修复。通过将识别出的错误作为反馈提供,LLM可以生成更正后的功能代码,从而提高初始输出。此外,通过结合上下文感知的验证和修复机制增强生成测试的鲁棒性,将确保测试套件与项目的特定结构紧密匹配。这种集成方法不仅自动化了错误纠正,还增强了测试生成过程的整体可靠性和有效性,更接近完全自动化、高质量测试套件生产的目标。
2. 变异测试中的性能
人工编写的测试在变异覆盖率方面始终优于LLM生成的测试,表明手动编写的测试在识别通过变异引入的代码变化方面更有效。为了解决这个问题,我们应该专注于通过改进提示算法和结合变异感知的测试生成技术来提高生成测试套件的鲁棒性。
3. 可扩展性和资源管理
自动化整个流程——下载项目、生成测试套件、集成库和执行评估——被证明是资源密集型的。高效管理和并行化这些任务可以减轻计算开销并提高可扩展性,使AgoneTest能够更有效地处理更大的数据集和代码库。
4. 提示技术的影响
提示技术的选择显著影响生成测试的质量。我们的实验表明,使用gpt-4的零样本提示产生了最佳结果,但性能在不同LLMs和提示的组合中有所不同。系统地探索和评估不同的提示策略将有助于识别最适合各种场景的配置。
5. 自动化上下文提取
向LLMs提供准确的上下文信息(如测试框架和Java版本)对于生成正确的测试类至关重要。自动化提取这些上下文信息减少了人工干预的需求,并提高了生成的提示和测试的质量。通过开发更复杂的解析器和上下文推理算法增强上下文提取的自动化,将动态适应各种项目配置。
6. 实际适用性
从GitHub上的实际开源Java仓库构建数据集确保了AgoneTest在真实场景中的运行。然而,确保数据集代表跨不同类型仓库和代码库的真实情况仍然是一个持续的目标。为了保持和提高实际适用性,我们应该不断升级和更新我们的数据集,以包括更广泛的真实仓库和项目结构,确保评估保持相关性和全面性。
这些经验教训指导我们进一步改进AgoneTest。通过实施这些改进,我们旨在开发一个更稳健、高效和可靠的自动化单元测试生成框架。
VII. 局限性
尽管AgoneTest提出了一个创新的框架,用于使用LLMs自动化生成和评估单元测试套件,但其当前实现和初步实验结果存在若干局限性。
A. 数据集与泛化性
在我们的评估中,我们依赖于新创建的Classes2Test数据集,该数据集源自Methods2Test。尽管该数据集旨在评估类级测试生成,但其范围仅限于Java项目。这使得我们的发现很难推广到其他编程语言。此外,Classes2Test中包含的仓库是基于其能够无错误编译的能力选择的,可能引入了对结构良好的代码库的偏见。
B. 模型与提示的变异性
测试的LLMs和提示数量有限:尽管AgoneTest支持多种LLMs和提示技术,我们的初始实验设置仅涉及两个模型(gpt-4和gpt-3.5 turbo)和两种提示类型(零样本和少样本)。随着LLMs和提示工程技术的不断发展,结果可能会因新模型和高级提示而有显著差异。我们初始测试的有限范围可能因此限制了结论的广度。
温度设置限制:在我们的实验中,我们将温度参数设置为0以确保一致性和可复现性。尽管这减少了随机性并增加了一致性,但它可能无意中限制了生成测试用例的创造力和多样性。不同的温度设置可能会产生更多样化的结果,但这在本研究中未进行探索。
C. 编译与执行失败
在我们的实验中观察到一个显著的局限性是某些生成的测试类的非编译和执行失败。大约66%的生成测试类在编译阶段被拒绝,或由于固有错误未能对指标做出积极贡献。这揭示了某些LLMs在一致生成语法和语义正确的测试代码方面的当前不足,影响了整体评估。
D. 评估指标
尽管我们采用了一套全面的指标和测试坏味道指示器,但这些指标本身可能无法完全捕捉测试套件的质量。
VIII. 结论与未来工作
在本文中,我们介绍了AgoneTest,这是一个使用LLMs自动化生成和评估单元测试套件的全面框架。该框架专注于生成复杂的类级测试套件,同时自动化从测试生成到集成和评估的整个测试过程。
我们的实验结果表明,AgoneTest可以在各种实际项目中生成和评估单元测试,提供关于不同LLMs和提示技术性能的详细见解。尽管初步结果令人鼓舞,但它们也突出了挑战,强调了进一步改进的必要性。
使用LLMs自动化单元测试生成是一个充满前景的领域。虽然当前的能力在某些任务(如变异覆盖率)上尚未达到人类工程师的水平,但在指令、行和方法覆盖率方面的良好结果表明,进一步的研究和改进可以弥合这一差距。未来的工作应专注于系统地研究最有效的LLMs和提示,同时持续改进针对常见问题的自动纠正机制。
参考文献
[参考文献部分内容与原文一致,此处不再重复翻译]