OpenQA

Industry & PracticeResearch & Benchmarks

利用大语言模型增强面向自然语言处理库的自动化单元测试生成(下篇)

智测团队 · OpenQA(openqa.cn)30 min read

利用大语言模型增强面向自然语言处理库的自动化单元测试生成(下)(中文全译)

In this piece

利用大语言模型增强面向自然语言处理库的自动化单元测试生成(下)(中文全译)

上篇:https://openqa.cn/articles/nlp-library-unit-tests-zh本篇从「## 5. 结果」继续。

5. 结果

本节呈现并讨论每个研究问题的结果。

5.1. RQ1:LLMSuite 相对基线的有效性

图 5(a–b)展示比较各评估工具行覆盖率与分支覆盖率的箱线图。在中位覆盖率上,LLMSuite 一致优于 CodaMosa-J、EvoSuite 与 LLMOnly 三者。分支覆盖率方面,LLMSuite 的中位数达到 68.60%,而 CodaMosa-J 为 58.03%,EvoSuite 为 53.33%,LLMOnly 为 32.64%。行覆盖率呈现类似趋势:LLMSuite 达到 73.49%,而 CodaMosa-J、EvoSuite 与 LLMOnly 分别为 65.62%、57.66% 与 35.94%。这些结果对应相对 CodaMosa-J,分支覆盖率提高 10.57%、行覆盖率提高 7.87%。相对 EvoSuite,平均而言分支覆盖率增益为 15.27%,行覆盖率增益为 15.94%。各项目的分支覆盖率、行覆盖率与变异得分见表 4。改进在所有项目上一致,不过 CoreNLP 的增益尤其显著。

我们使用 Wilcoxon 符号秩检验评估统计显著性。结果表明,LLMSuite 生成的测试用例在分支与行覆盖率上显著高于 CodaMosa-J 与 EvoSuite 生成的测试(p 值 ≪ 0.05),也显著优于 LLMOnly(p 值 ≪ 0.05)。精确数值见表 5。与 CodaMosa-J(0.73)和 EvoSuite(0.72)比较时 A₁₂ 效应量为大效应,与 LLMOnly 比较时更大(0.85),置信区间一致位于 0.5 之上。由于每个工具对每个类运行五次,我们进行了逐类显著性检验。图 5(d–f)展示所得胜率模式。分析揭示出清晰趋势:相对 CodaMosa-J,LLMSuite 在 38 个类上获得更高分支覆盖率(仅在 3 个类上更低);相对 EvoSuite,在 36 个类上更高(仅 2 个类更低);相对 LLMOnly,在 59 个类上更高(仅 3 个类更低)。

表 4. 各项目中位分支覆盖率、中位行覆盖率与中位变异得分比较

项目类数分支 LLS分支 CM分支 ES分支 LLM行 LLS行 CM行 ES行 LLM变异 LLS变异 CM变异 ES变异 LLM
CoreNLP4248.5%33.69%29.8%16.01%61.62%42.03%41.66%28.47%43.08%17.39%35.47%33.25%
OpenNLP1788.84%82.47%81.97%47.7%92.37%86.77%86.58%62.37%70.0%68.0%60.50%39.5%
Mallet1678.79%72.84%77.62%44.97%84.03%78.88%82.12%56.77%89.82%81.69%80.21%26.37%
Gate1153.32%52.61%49.3%27.82%58.09%57.20%54.72%25.87%73.0%70.0%72.5%59.0%
CogCompNLP1437.63%35.48%31.71%17.95%44.27%42.99%43.31%25.82%62.0%52.0%62.0%38.0%
总体10068.60%58.03%53.33%32.64%73.49%65.62%57.50%43.00%63.0%52.0%58.0%38.26%

表中 LLS 为 LLMSuite,CM 为 CodaMosa-J,ES 为 EvoSuite,LLM 为 LLMOnly。

在图 5(d)中,我们观察到 LLMSuite 在变异得分上一致优于 CodaMosa-J、EvoSuite 与 LLMOnly,尽管效应量比结构覆盖率更为温和。在中位数上,LLMSuite 的变异得分为 63.0%,CodaMosa-J 为 52.0%,EvoSuite 为 58.0%,LLMOnly 为 38.26%。这对应相对 CodaMosa-J 提高 11%,相对 EvoSuite 提高 5%,相对 LLMOnly 提高约 24.7 个百分点。

这些差异具有统计显著性:Wilcoxon 符号秩检验给出的 p 值为 0.0003(相对 CodaMosa-J)、0.007(相对 EvoSuite)与 0.002(相对 LLMOnly)。A₁₂ 效应量分别为 0.58、0.57 与 0.64,表明优势一致但更为温和,所有 95% 置信区间均保持在 0.5 之上。综合来看,这些结果表明 LLMSuite 的变异体杀死率高于基线,尽管改进幅度相对结构覆盖率指标更为温和。

尽管有这些增益,我们观察到 LLMSuite 在变异得分上的收益不如结构覆盖率那样大。我们将其归因于:LLMSuite 依赖 EvoSuite 生成断言,因而无法利用 LLM 能够产生的语义上更准确的断言。还值得注意的是,由于时间限制,LLMSuite、CodaMosa-J 与 EvoSuite 都没有配置为强变异或弱变异。尽管如此,LLM 生成的测试片段可以有效提升 EvoSuite 的断言质量。

对 RQ1 的回答: LLMSuite 在中位分支覆盖率、行覆盖率与变异得分上优于 CodaMosa-J、EvoSuite 与 LLMOnly。不过,变异得分的增益相对分支与行覆盖率的改进更为温和。

表 5. LLMSuite 相对基线的效应量与显著性(按指标)

比较指标p 值显著A₁₂95% CI
LLMSuite vs. CodaMosa-J分支1.6×10⁻⁹是0.73[0.66, 0.78]
LLMSuite vs. CodaMosa-J行3.9×10⁻¹¹是0.74[0.68, 0.80]
LLMSuite vs. CodaMosa-J变异0.0003是0.58[0.53, 0.65]
LLMSuite vs. EvoSuite分支5.5×10⁻⁹是0.72[0.68, 0.80]
LLMSuite vs. EvoSuite行3.7×10⁻¹⁰是0.80[0.60, 0.92]
LLMSuite vs. EvoSuite变异0.007是0.57[0.50, 0.63]
LLMSuite vs. LLMOnly分支1.9×10⁻²¹是0.85[0.79, 0.90]
LLMSuite vs. LLMOnly行1.6×10⁻²⁹是0.85[0.79, 0.90]
LLMSuite vs. LLMOnly变异0.002是0.64[0.54, 0.74]

就分支覆盖率而言:

  • LLMSuite 优于 CodaMosa-J 的类数(95% CI 下界 > 0.5):38(38%)
  • LLMSuite 劣于 CodaMosa-J 的类数(95% CI 上界 < 0.5):3(3%)
  • LLMSuite 优于 LLMOnly 的类数(95% CI 下界 > 0.5):59(59%)
  • LLMSuite 劣于 LLMOnly 的类数(95% CI 上界 < 0.5):3(3%)
  • LLMSuite 优于 EvoSuite 的类数(95% CI 下界 > 0.5):36(36%)
  • LLMSuite 劣于 EvoSuite 的类数(95% CI 上界 < 0.5):2(2%)

5.2. RQ2:设计选择的影响

该研究问题考察不同设计决策如何影响本方法的有效性,以分支覆盖率衡量。具体而言,我们分析更换 LLM、使用不同的基于搜索的策略、使用不同上下文层级(方法级与类级)、提示策略以及 LLM 调用次数的影响。CodaMosa-J 基线也可视为一个变体,因为它使用不同的基于搜索的算法与提示策略。不过,我们将其排除在本 RQ 之外,因为它已在 RQ1 中详细考察。表 6 报告每个变体的均值与中位覆盖率,以及它们相对 EvoSuite 的差异。

图 6. 不同 LLMSuite 变体相对 EvoSuite 的逐类分支覆盖率改进(Δ)直方图

为评估每个设计选择的贡献,我们以 EvoSuite 为基线计算性能差值(Δ),并把这些结果与上一节介绍的 LLMSuite 默认配置比较。图 6 展示每个变体在各个类上的 Δ 覆盖率分布。统计分析确认,默认 LLMSuite 配置的覆盖率显著高于每个变体(p 值 ≪ 0.05),除相对 Zero-Shot 变体外,所有比较的效应量均为大效应(A₁₂ > 0.71);相对 Zero-Shot 变体的效应量较小(A₁₂ = 0.60)。

不同上下文层级的影响。 与采用类级提示的默认 LLMSuite 不同,该变体使用方法级提示,以评估在同一底层 LLM(ChatGPT)下上下文层级(方法级与类级)的影响。平均而言,分支覆盖率相对 EvoSuite 提高 +3.76%,但差异不具统计显著性(p = 0.4)。然而,LLMSuite 的默认类级配置显著优于方法级变体(p ≪ 0.05),凸显更丰富的类级上下文的好处。

表 6. 所有变体的分支覆盖率指标

指标EvoSuiteOrig.S. Pool1xLlama0-ShotMosaM. Level
均值56.48%63.39%55.08%61.85%47.82%61.29%60.22%60.23%
中位数60.12%74.81%58.80%70.00%42.65%67.67%68.60%62.53%
Δ 均值–+6.91%−1.40%+5.37%−8.66%+4.82%+3.74%+3.76%
Δ 中位数–+14.70%−1.32%+9.88%−17.46%+7.56%+8.49%+2.42%

使用不同上下文层级的本地 LLM 的影响。 我们用 CodeLLaMA 13B 替换 ChatGPT,以考察使用不同 LLM 以及不同上下文层级(方法级与类级)的影响。由于 CodeLLaMA 的上下文窗口限制,它在方法级而非类级被提示,但使用同一解析器。平均而言,分支覆盖率相对 EvoSuite 下降 8.6%,大多数类覆盖率更低,只有少数类略有改进(图 6)。相对 ChatGPT,我们假设 CodeLLaMA 在生成能够提高覆盖率的测试用例方面效果较差。这可能是因为 CodeLLaMA 的响应时间比 ChatGPT 慢得多,留给搜索过程探索测试空间的时间更少。

提示策略的影响。 我们评估了零样本提示变体 0-Shot,以评估提示策略的影响。与使用自我精炼机制的默认 LLMSuite 不同,该变体一次性生成测试用例,不做迭代精炼。0-Shot 相对基线(EvoSuite)的平均分支覆盖率改进为 +4.82%,具有统计显著性(p 值 = 0.004)。然而,默认 LLMSuite 仍然显著优于零样本变体(p 值 = 0.019),确认了自我精炼在提示生成中的好处。

减少 LLM 测试生成调用次数的影响。 为评估重复 LLM 交互的作用,我们把默认 LLMSuite(对每个目标进行五次 LLM 调用,L = 5)与只使用一次调用(L = 1)的简化变体(记为 1x)进行比较。该变体相对 EvoSuite 的分支覆盖率改进为 +5.37%,具有统计显著性(p 值 = 0.002)。这些结果表明,即使单次 LLM 调用也能带来显著的覆盖率增益。不过,多次调用能进一步提高性能(p 值 ≪ 0.05),说明迭代提示的好处。

只改进字符串输入的影响。 我们考察是否只增强字符串输入会影响覆盖率。EvoSuite 使用随机值的静态池,以及通过插桩收集的值来生成字符串。在该变体中,我们用针对被测类定制的、由 LLM 生成的字符串替换静态池,而不使用 LLM 生成的测试来引导搜索过程。结果表明,仅这一改变并不能显著提高覆盖率;事实上,分支覆盖率相对 EvoSuite 略降 1.4%。这说明孤立地改进字符串输入是不够的——测试生成过程的其他方面也需要处理。

使用不同 SBST 策略的影响。 与把 DynaMOSA 作为搜索算法、并配合标准提示与自我精炼策略的默认 LLMSuite 不同,该变体用 MOSA 替换 DynaMOSA,以评估底层 SBST 算法的影响。平均而言,分支覆盖率相对 EvoSuite 提高 +3.74%,但差异不具统计显著性(p = 0.3)。然而,默认 LLMSuite 配置显著优于基于 MOSA 的变体(p ≪ 0.05),表明更强的搜索策略更能利用 LLM 生成的代码片段,并将其转化为额外的覆盖率增益。

对 RQ2 的回答: 默认 LLMSuite 配置优于我们所考察的 6 个变体——它们分别采用不同的 LLM、不同的上下文层级、不同的提示策略、更少的对 LLM 的提示次数、只改进字符串输入,以及不同的 SBST 策略。

5.3. RQ3:相对 SBST 方法的改进

我们分析了 NLP 库的哪些组件被 EvoSuite 生成的测试、基于 LLM 的测试以及 LLMSuite 高度覆盖。图 7 表明,LLMSuite 在所有类别上一致优于两个基线——EvoSuite 与 LLM-Tests——唯独机器学习类别出现 0.1% 的边际下降。

图 7. 按不同功能划分的覆盖率比较

LLMSuite 在各功能类别上取得的平均分支覆盖率改进为:信息抽取(18%)、共指(8%)、切分(7%)、规范化(5%)与语言标注(3%)。增益在文本密集、涉及语言特定处理的类中尤其突出,而不是那些聚焦数值计算、数据结构或工具逻辑的类。为更好理解这一点,我们分析了差异最显著的类。

使用 LLM 如何改进基于搜索的测试生成。 我们的分析揭示,相对传统基于搜索的方法,LLM 以三种主要方式提升 LLMSuite 中的覆盖率:

// A. EntityMentionsAnnotator 类中的一种非法行为 - CoreNLP 项目
// if anything is still at 1.1, set it to -1.0
for (String label : entityLabelProbVals.keySet()) {
    if (entityLabelProbVals.get(label) >= 1.1) {
        entityLabelProbVals.put(label, -1.0);
    }
}
// —————————————————————————-
// B. 一个有助于 LLMSuite 提高覆盖率的 LLM 生成测试用例
@Test
public void testDetermineEntityMentionConfidences() {
    CoreLabel token = new CoreLabel();
    token.setWord("Barack");
    token.set(NamedEntityTagProbsAnnotation.class, Map.of("PERSON", 0.95, "LOCATION", 0.05));
    CoreMap entityMention = new ArrayCoreMap();
    entityMention.set(TokensAnnotation.class, List.of(token));
    EntityMentionsAnnotator.determineEntityMentionConfidences(entityMention);
    …
}

图 8. EntityMentionsAnnotator 的示例场景

A. 测试数据与对象调用序列。 在许多测试用例中,LLM 为构造对象并以正确顺序调用它们提供了有用指导。这在需要复杂设置的类中尤其重要:由于搜索空间很大,缺乏信息的搜索可能陷入局部最优。例如,对 CoreNLP 项目中的三个类,QuoteAnnotator(改进 64%)、EntityMentionsAnnotator(41%)与 WordsToSentencesAnnotator(39%),改进主要来自 LLM 生成正确的实例化与方法调用序列。

图 8 的条目 B 展示了 EntityMentionsAnnotator 的一个代表性示例。该测试构造一个 CoreLabel 词元,赋予实体类型概率(例如 PERSON 为 0.95),将其包装进 ArrayCoreMap,并调用目标方法。由于对象之间的特异性与相互依赖,EvoSuite 难以完成此类设置。这种指导也使探索依赖行为的分支成为可能,例如对非法概率的错误处理,如图 8 条目 A 所示。

B. 生成领域特定的文本输入。 许多 NLP 类依赖结构化或语言上丰富的输入,随机生成无法触发这些输入。我们观察到,LLM 生成的测试片段使 LLMSuite 能够更好地执行句法分析、切分与规范化逻辑。例如,OpenNLP 的 Parser 类期望 Treebank 风格的输入结构。LLMSuite 能够生成此类输入,如下例所示,从而激活 EvoSuite 遗漏的分支:

Parse parse0 = Parse.parseParse("(TOP (NP (NN -LRB-) (NN book) (NN -RRB-)))");

C. 推断配置与属性设置。 有些类依赖外部属性或配置来激活某些分支。例如,在 WordsToSentencesAnnotator 中,某些 HTML 边界处理逻辑只有在设置了适当配置时才会触发,如代码清单 9 所示。

// A. LLMSuite 生成的测试用例行
properties0.setProperty("ssplit.htmlBoundariesToDiscard", "div,be");
// —————————————————————————–
// B. CoreNLP 项目 - WordsToSentencesAnnotator.java
// HTML boundaries which are discarded
bounds = properties.getProperty("ssplit.htmlBoundariesToDiscard");
if (bounds != null) {
    String[] elements = bounds.split(",");
    htmlElementsToDiscard = Generics.newHashSet(Arrays.asList(elements));
}

图 9. WordsToSentencesAnnotator 的示例场景

LLM 测试提供较少指导之处。 在以数值计算或算法逻辑为主的类中,例如 KMeans,LLMSuite 的收益有限。在这些场景中,LLM 常常无法生成有效的数值输入,或出现幻觉;我们假设这会误导测试生成过程,或使其陷入局部最优。因此,LLM 指导的附加价值很小,在某些情况下测试质量还可能略有下降。

对 RQ3 的回答: 当涉及提供测试数据、把对象调用序列安排正确,以及推断配置与属性设置时,LLMSuite 改进了 SBST。在以数值计算与算法逻辑为主的类中,LLMSuite 的收益有限。

5.4. RQ4:相对人工测试的改进

图 10. 各项目中人工编写测试用例与 LLMSuite 生成测试用例所达到的分支覆盖率与变异得分比较。

在我们数据集的 100 个类中,只有 27 个拥有专门的人工编写测试用例;其余 73 个可能被其他测试间接覆盖。GATE 中的人工测试使用 JUnit 3,OpenNLP 使用 JUnit 5,其余三个项目——CoreNLP、Mallet 与 CogCompNLP——主要使用 JUnit 4。因此,我们使用各项目自身的设置配合 JaCoCo 与 PITest 运行,而不是通过我们统一的覆盖率工具。我们首先考察 LLMSuite 生成的测试与人工编写测试在分支覆盖率与变异得分上如何比较,以及它们如何补充现有人工测试套件。然后通过对这些缺口分类,分析人工编写测试遗漏的区域,并由此导出一个分类体系。

表 7. 各项目人工编写测试、LLMSuite 生成测试及其组合的分支覆盖率与变异得分比较

项目类数分支 Man分支 LLS分支 Man+LLS分支 Δ变异 Man变异 LLS变异 Man+LLS变异 Δ
CoreNLP747.53%76.42%85.60%+38.08%43.02%62.42%67.37%+24.35%
OpenNLP755.67%76.57%86.63%+30.97%47.63%59.11%65.37%+17.75%
GATE620.61%61.69%64.82%+44.21%25.82%54.79%56.22%+30.40%
Mallet518.49%86.47%86.55%+68.06%26.47%76.04%79.03%+52.56%
CogCompNLP217.6%22.65%29.56%+11.96%27.51%31.03%47.49%+19.98%
总体2733.99%68.49%74.55%+40.55%38.1%58.06%63.54%+25.44%

#### 5.4.1. 增量覆盖率与变异得分

图 10 以箱线图比较人工编写测试用例与 LLMSuite 生成测试所达到的分支覆盖率与变异得分。如表 7 所报告,人工编写测试的平均分支覆盖率为 33.99%,而 LLMSuite 生成的测试达到 68.49%。两者合并后,测试套件达到 74.55%,对应 27.7% 的改进。

变异得分可观察到类似模式。人工编写测试达到 38.1%,LLMSuite 生成的测试达到 58.06%,合并套件达到 63.54%,对应 25.44% 的改进。总体而言,LLMSuite 在分支覆盖率与变异得分两方面都补充了人工编写测试,尽管其效应在分支覆盖率上更为突出。

这一观察与 RQ1 的发现一致:我们指出 LLMSuite 依赖 EvoSuite 生成断言。因此,它从 LLM 生成的断言中获益较少,这可以解释为何变异得分的改进小于分支覆盖率的改进。

此外,人工测试的质量与完整性在各项目间有所不同。积极维护的项目,如 OpenNLP 与 CoreNLP,通常拥有更全面的测试套件。相比之下,GATE、Mallet 与 CogCompNLP 的覆盖率较低。

#### 5.4.2. 所发现缺口的分类体系

为调查 LLMSuite 如何补充现有测试覆盖,我们人工分析了每个项目及其对应测试用例,以确定生产代码的哪些部分被人工编写测试执行、哪些区域仍未被测试。为更好理解这些未覆盖区域,我们人工审阅了数据集中未覆盖的代码块,并对缺乏覆盖的原因进行分类。为此,我们采用 Wang 等(Wang 等,2021)的分类方案,该方案最初用于分析机器学习库中的测试缺口。我们通过增加两个新类别——方法重载(MEO)与依赖属性的行为(PRB),以星号(*)标记——并移除在我们数据集中不常见的消息处理行为(MEB)类别,对其方案进行了改造。该分析表明,在许多情况下,LLMSuite 能够把覆盖扩展到人工测试套件未到达的代码块。下面我们用 CoreNLP 项目中的例子说明已覆盖与未覆盖的行为。

人工编写测试覆盖了什么。 人工测试倾向于通过高层场景关注类的主要功能,常常在单个测试方法中组合多种用例。例如,CleanXmlAnnotator 类处理 XML 输入,并从 <post>、<quote> 以及 <img/> 等自闭合标签中抽取信息。这些标签可能包含 author、datetime 或 document_id 等属性。代码清单 11 给出一段示例 XML。该类的人工测试覆盖了一些合法情形,例如带属性、格式良好的帖子与引语,以及若干非法情形,如未闭合标签。这些是有意义的场景,但并未覆盖全部可能行为。

<post author="UDDep" datetime="2010-05-30T15:43:00" id="p2">
 <quote orig_author="James Rood">
 Yesterday afternoon as I negotiated route 149 from Lake George to Fort Ann
 in NY I passed a new diner that had opened that day.
 <img src="http://britishexpats.com/forum/images/smilies/wink.gif"/>
 </quote>
 If they don’t have english food and beer…tell em…
</post>

图 11. CleanXMLAnnotator 的示例场景

人工编写测试未覆盖什么。 相比之下,人工编写测试常常省略替代执行路径、特殊输入配置以及不那么核心的行为。下面我们用 CoreNLP 项目中的例子描述每个缺口类别:

合法行为(VB)。 一个类往往支持多种合法行为,但人工测试只执行其中子集。例如,在 CleanXMLAnnotator 中,测试检查 XML 文档是否以特定标签开始和结束,以及标签是否包含属性。然而,它们只覆盖少数特定标签,许多受支持的标签仍未被测试。这种部分覆盖解释了为何有些行仍然未覆盖。在所有类中,有 530 个代码块因此未被覆盖,占全部未覆盖块的 49%。使用 LLMSuite 后,此类块只剩 222 个,主要是因为初始化复杂对象存在挑战。

非法行为(IVB)。 除合法行为外,当给定超出预期域的输入时,类也可能表现出非法行为,例如为参数提供超出允许范围的值。人工编写测试覆盖了一些情形(例如代码清单 11 中未闭合的 XML 标签),但常常遗漏其他情形,例如闭合标签多于开始标签。这导致人工编写套件出现 56 次覆盖下降。相比之下,LLMSuite 通过更好地处理此类边界场景,将其减少到仅 16 例。

异常处理(EX)。 人工编写测试常常不覆盖异常。例如,在 CleanXMLAnnotator 中,当缺少适当的标签标注时会抛出异常,但这并未被测试。我们在人工套件中发现了 56 个此类异常情形,而 LLMSuite 将该数字减少了 11。

辅助方法(AUX)。 我们的分析表明,许多工具方法或辅助方法在人工编写测试中仍未被测试,主要因为它们被声明为私有,且在典型测试场景中不被直接调用。这留下 266 个未覆盖块,约占全部未覆盖代码的 25%。虽然 LLMSuite 与基于搜索的技术有时可以间接到达这些方法,测试私有代码仍然具有挑战,因为字节码插桩并不提供关于它们的信息。

方法重载(MEO)。 构造方法与方法可以有多个重载变体,各自具有不同的参数列表。在我们的数据集中,有 121 个代码块因为并非所有重载都被人工编写测试执行而仍未覆盖。LLMSuite 中的基于搜索的过程把这一数字减少到仅 7 个块。

依赖属性的行为(PRB)。 NLP 库中的某些功能依赖特定属性——如配置文件、模型路径或输入文档——来激活某些代码路径。例如,在 CleanXMLAnnotator 中,不同的属性值触发不同的执行分支。这些场景在人工编写测试中常常被遗漏,留下 48 个未覆盖块。LLMSuite 将其减少到 10 个,但由于所需的领域知识,处理此类情形仍然具有挑战。

对 RQ4 的回答: 把人工编写的测试用例与 LLMSuite 生成的测试用例相结合,能显著提高覆盖率与变异得分。我们识别出 6 种 LLMSuite 能够对额外测试覆盖作出有力贡献的具体情形。

表 8. 每个 NLP 库中未覆盖代码的分布

项目工具VB(%)IVB(%)EX(%)AUX(%)MEO(%)PRB(%)
CogComp-NLPLLS3(18)1(6)0(0)0(0)0(0)13(76)
CogComp-NLPMan6(46)3(23)1(8)0(0)0(0)3(23)
CoreNLPLLS33(62)3(6)5(9)3(6)0(0)9(17)
CoreNLPMan41(30)9(7)10(7)16(12)31(23)30(22)
GateCoreLLS114(33)10(3)23(7)179(53)3(1)12(4)
GateCoreMan261(39)36(5)30(4)209(31)48(7)8(1)
MalletLLS15(54)0(0)2(7)10(36)0(0)1(4)
MalletMan154(70)6(3)7(3)27(12)40(18)0(0)
OpenNLPLLS57(60)2(2)3(3)23(24)4(4)4(4)
OpenNLPMan68(68)2(2)6(6)14(14)2(2)7(7)
总体LLS222(42)16(3)33(6)215(40)7(1)38(7)
总体Man530(49)56(5)54(5)266(25)121(11)48(4)

6. 讨论

测试生成时间预算。 我们为每个目标类分配了 15 分钟的时间预算。在该时间预算内,带自我精炼的基于 LLM 的测试生成平均消耗 148.2 秒(零样本模式下为 16.1 秒)。当时间有限时,这会减少可用于搜索过程的时间。不过,由于 LLM 测试生成独立于搜索,可以通过与 SBST 过程并行运行来避免这一开销。

成本分析与词元用量。

图 12. 对同时具有类级零样本与类级自我精炼全部结果的 65 个共同类,类级自我精炼的词元用量分析。

除增加处理时间外,还必须考虑基于 LLM 的测试生成的实际成本。我们分析了实验所选取的 100 个类中、类级零样本生成与类级自我精炼全部结果均可用的 65 个共同类上的词元用量。如图 12(a)所示,完整的自我精炼流水线在 455 次 LLM 调用中消耗 7,547,600 个词元,而类级零样本在 65 次调用中需要 688,549 个词元。假设定价模型为每百万输入词元 5.00 美元、每百万输出词元 15.00 美元,这对应自我精炼的估计总成本为 46.62 美元,零样本为 4.78 美元,即每个目标类大约 0.72 美元对 0.07 美元。

这一开销大部分来自自我精炼本身。图 12(a)表明,在总计 7,547,600 个词元中,6,082,787 个(80.6%)由自我精炼轮次消耗,而初始测试生成与测试重排版分别占 642,430 个(8.5%)与 822,383 个(10.9%)。图 12(b)进一步表明,词元用量随轮次稳步增长,从第 1 轮的 952,357 增长到第 5 轮的 1,485,163。这与保留聊天历史时提示增长的情形一致。例如,在 CoreNLP 的 RelationFeatureExtractor 类中,输入词元数在各精炼迭代中从 1572 增加到 3325、5182、6996 与 8684,而输出规模相对稳定,约为 1727 个词元。

图 12(c)还显示各项目之间的差异。词元用量对应 CoreNLP 的 27 个目标类、Mallet 的 14 个、GATE 的 7 个、OpenNLP 的 8 个,以及 CogComp-NLP 的 9 个。归一化之后,每类平均词元用量为:CoreNLP 122,457,Mallet 115,475,GATE 134,482,OpenNLP 112,549,CogComp-NLP 86,984,表明词元成本也取决于类的复杂度与提示规模。

总体而言,这些结果表明,迭代精炼的额外增益伴随着可观的词元与金钱成本。因此,精炼迭代次数(T)的选择不仅应由有效性增益引导,也应由成本效益引导。在实践中,较少的精炼轮次可能比使用完整迭代过程提供更好的权衡。

解析器。 如第 3 节所讨论,我们的解析器 LLM2EvoSuiteParser 能够处理嵌套方法调用、可变参数与多维数组等复杂情形。尽管如此,EvoSuite 与 Spoon 两方面的局限使 LLMSuite 无法充分利用所有 LLM 生成的测试片段。图 13 汇总了 LLM 生成语句经过幻觉过滤与解析阶段的流向,并展示它们如何被过滤为不同结果。

在解析之前,生成的代码经过幻觉过滤阶段。在 LLM 组件中,我们首先尝试使用前文所述的启发式方法修复生成的片段。因为使用“循环中的 LLM”修复机制在计算上过于昂贵,我们只依赖基于启发式的修复,并尽量保留尽可能多的生成代码。在 474,626 条 LLM 生成语句中,我们发现 39,980 条幻觉语句,包括 API 不匹配。我们不是丢弃整个测试用例,而是把幻觉行连同因此变得无效的依赖行一起注释掉,同时保留其余合法语句,使它们仍能对测试有所贡献。这留下 434,646 条无幻觉且可编译的语句,意味着 91.57% 的生成行仍然可用。这些清理后的测试被当作 LLMOnly 测试用例,我们独立测量其覆盖率,然后把它们交给 LLM2EvoSuiteParser,转换为与 EvoSuite 兼容的格式。

在 434,646 条无幻觉语句中,388,761 条被成功转换,对应 89.44% 的转换成功率。其余语句中,2,461 条无法解析,因为它们包含 EvoSuite 不支持的构造,包括 for 循环(691)、foreach 循环(605)、while 循环(515)、if 语句(344),以及其他 306 种不支持的构造,例如类或接口的新实现。此外,43,424 条语句在解析期间无法解析。其中大多数(40,393)涉及 EvoSuite 在测试生成过程中不支持的 API 调用,例如 when()、spy() 与 fail()。其余 3,031 条语句包括不支持的二元或一元表达式,例如数学运算或 append() 之类的字符串操作。

EvoSuite 还会在搜索阶段之后插入额外构造——如 catch 块、断言与 mock 参数初始化——这使某些 LLM 生成的行无法被直接翻译。另一方面,Spoon 有时无法解析未显式定义的类型。为解决这一问题,我们采用宽松的类型检查,覆盖了其中大多数情形。我们还观察到,EvoSuite 在处理带依赖泛型的参数化类型时,把测试用例转换为源代码存在问题,例如 class Value<J, T extends Type>,其中第二个类型依赖于第一个类型。

图 13. LLM 生成语句经过清理与解析流水线的流向。

LLM 幻觉。 已知 LLM 会在输出中产生幻觉(Yao 等,2023)。在代码生成语境中,这通常表现为引用不存在的 API(Eghbali 与 Pradel,2024)。作为幻觉过滤阶段的一部分,我们首先尝试用启发式方法修复此类问题;若无法修复,则滤除幻觉行。在所有运行中,我们在 474,626 条生成行中观察到 39,980 条幻觉行(图 13)。

为更好理解这些幻觉的性质,我们对一次运行中 70 个目标类的生成测试用例进行了详细分析。在该子集中,我们识别出 8,449 条幻觉行。我们把这些幻觉分为五类:未解析引用(3,087 例,约 36.5%)、访问修饰符问题(1,621,约 19.2%)、抽象实例化错误(1,573,约 18.6%)、类型不匹配或不兼容类型(1,177,约 13.9%),以及其他(991,约 11.7%)。我们还调查幻觉频率与覆盖率改进之间是否存在相关:我们计算 ΔCoverage 与“被注释的幻觉行数相对已执行测试数之比”之间的 Pearson 相关系数(ΔCoverage ∼ 被注释错误数 / 已执行测试数)。结果为 −0.273,表明弱负相关。这说明每个测试幻觉更多的类,在覆盖率改进上从 LLMSuite 获益较少。

训练污染。 ChatGPT-4o-latest-2025-07-12 在大规模公开代码语料上训练,包括 GitHub。由于 OpenAI 并未披露确切训练数据,无法确认我们数据集中的任何代码是否被纳入。为评估潜在重叠,我们使用代码相似性检测工具 JPlag(Prechelt 等,2002),比较 LLM 生成的测试用例与人工编写的测试用例。结果表明,平均相似度的平均值为 4.48%,最大相似度的平均值为 12.05%,两者都相对较低。此外,如第 5.4 节所讨论,我们数据集中只有 30 个类具有对应的人工编写测试用例。在相似度得分较高的少数情形中,我们选取前五名进行人工检查。该分析未发现有意义的重叠,表明 ChatGPT 并非在复制人工编写的测试用例。

LLMSuite 的实践价值与范围。 尽管 LLMSuite 提高了结构覆盖率,这些增益应谨慎解释。EvoSuite 等基于搜索的单元测试技术旨在进行结构探索,而非基于规约的验证(Fraser 与 Arcuri,2013a;Fraser 与 Arcuri,2014)。特别是,基于回归的预言捕获的是观察到的行为,而不一定是预期功能,因此并不保证语义正确性。生成的测试因而应被视为对人工编写或基于规约的测试的补充。

覆盖率也只是测试有效性的一个代理(Inozemtseva 与 Holmes,2014)。更高的覆盖率增加了执行多样化行为的机会,但并不必然意味着更强的故障检测能力。因此,我们用变异得分补充覆盖率,它为故障检测能力提供互补证据(Just 等,2014;Shamshiri 等,2015;Andrews 等,2005;Papadakis 等,2018)。

我们也承认,许多工业测试挑战出现在更高层次,如集成测试、系统测试与验收测试(Arcuri,2018b;Openja 等,2024;Arcuri,2018a)。尽管如此,对复杂库而言,自动化单元测试生成仍然有用,因为人工探索边界情形很困难。在本工作中,RQ3 与 RQ4 通过展示 LLMSuite 如何通过执行额外代码区域与行为来补充 SBST 与人工编写测试,考察了该方法超出覆盖率本身的实践价值。这些结果表明,该方法的主要收益不仅在于更高的覆盖率,也在于更广的行为探索,尤其是在现有技术表现挣扎的情形中。

7. 对效度的威胁

我们识别出以下对结果效度的威胁:

内部效度。 威胁可能来自 LLMSuite 实现或评估流水线中的错误。虽然前文讨论了一些局限,我们人工核验了关键组件,编写测试用例以验证正确性,并检查了部分结果。此外,我们通过重新运行实验并审阅离群值,交叉验证了覆盖率与变异得分。

外部效度。 我们评估了来自五个开源 NLP 库的 100 个类,系统选取以覆盖多样化功能。虽然这提供了一个多样且具有挑战性的样本,它并非旨在代表 NLP 库中的所有类。相反,我们的基准聚焦结构复杂、难以覆盖的单元。这一设计选择是有意的,因为此类非平凡类通常更难、成本更高,不适合完全依赖人工测试,因而更适合评估自动测试生成的实践收益(Panichella 等,2018b)。结果也可能无法推广到其他软件领域,例如机器学习。此外,虽然我们使用了在开源代码上训练的 LLM(GPT-4o、CodeLLaMA-13B),相似性分析未发现与人工编写测试的有意义重叠,表明训练数据泄漏(Sallou 等,2024)并未影响我们的结果。

构念效度。 虽然我们使用行/分支覆盖率与变异得分等标准指标评估测试有效性,它们并不能完全捕获测试可读性或可维护性等定性方面,也不能完全捕获检测真实世界故障的能力。此外,尽管我们如第 4 节所述把 CodaMosa-J 实现为原始 CodaMosa 的 Java 对应物,它可能无法完美地精确复现原始框架,从而引入潜在的效度威胁。

结论效度。 为确保比较的可靠性,我们遵循评估基于搜索的算法的既有指南(Arcuri 与 Briand,2014)以及涉及 LLM 的研究(Sallou 等,2024)。我们使用了适当的统计检验,如 Wilcoxon 符号秩检验,以及效应量度量。然而,LLM 输出的变异性以及基于搜索的方法固有的非确定性会给结果引入噪声。为缓解这一点,我们在多次运行上取平均结果,并在适用处分析置信区间。

8. 相关工作

8.1. 面向 NLP 库的测试用例

如前所述,测试 NLP 与 ML 库有其自身的一组挑战,不同于传统软件系统。数据的复杂性与可能场景的广泛范围使测试这些库特别困难。因此,它们的单元测试常常表现出较低的覆盖率与变异得分,而偏见、公平性与安全等重要方面也没有被一致地评估(Openja 等,2024;Wang 等,2021)。

研究者曾以不同方式尝试解决这些问题。例如,NLPLego(Ji 等,2025)通过检查 NLP 模型在结构化输入变换下是否产生预期输出,改进蜕变测试。它通过生成多样且合法的输入,帮助揭示细微的不一致性。为改进单元级测试生成,其他研究(Olsthoorn 等,2021;Krodinger 等,2025)探索把基于文法的模糊测试与 SBST 相结合。有些聚焦处理 XML 与 JSON 等结构化输入(Olsthoorn 等,2021),另一些为 TensorFlow 与 PyTorch 等复杂 ML 框架生成类型一致的输入(Krodinger 等,2025)。然而,这些方法通常限于狭窄领域,并依赖手工编写的文法或定制类型规约,从而降低了可扩展性与普遍适用性。

LLMSuite 通过使用 LLM 在单元测试层面生成复杂且多样的测试输入(包括边界场景)来应对这些局限。LLMSuite 并不针对单一数据格式,而是适应多种输入结构。虽然该方法可推广到其他领域,本研究聚焦 NLP 库,因为它们文本密集,并对自动化测试提出独特挑战。

8.2. 基于 LLM 的测试用例生成

已有若干仅使用 LLM 的测试生成器,从简单工具——如 Siddiq 等的方法(Siddiq 等,2024),仅依赖 LLM 产出测试——到稍更先进的工具,如 ChatTester(Yuan 等,2024),它引入了基本的编译器错误修复循环。TestSpark(Sapozhnikov 等,2024)、CoverUp(Pizzorno 与 Berger,2024)与 ChatUniTest(Chen 等,2024)等更复杂的系统丰富提示或迭代修复错误,以提高测试质量。然而,这些方法完全依赖 LLM,并不集成 SBST,且它们都不聚焦 NLP 库这类领域特定软件。先前工作也显示结果差异很大:Siddiq 等报告在 SF110 数据集上只有 2% 的覆盖率,而 TestPilot(Schäfer 等,2024)在小型 JavaScript 系统上达到约 70% 的语句覆盖率,其他研究则旨在改进人工编写的测试。少数混合方法——如 UTGen(Deljouyi 等,2025)与 Aster(Pan 等,2025)——把 LLM 与 SBST 结合,但主要是为了提高可理解性与可读性。

更近期的工作把 LLM 与互补的引导机制相结合,而不是单独依赖它们。Yang 等(Yang 等,2025)提出一种由程序分析引导的方法,使用依赖信息与反例把 LLM 导向难以覆盖的分支,报告相对先前 SBST 与基于 LLM 的基线有很强的覆盖率改进。类似地,Broide 与 Stern(Broide 等,2025)提出 EvoGPT,一种混合方法:使用 LLM 生成多样的初始种子,然后应用进化搜索来提高覆盖率与变异得分。这些研究进一步支持如下观点:当 LLM 与互补技术集成时最为有效。LLMSuite 遵循同一总体方向,但不同之处在于聚焦 NLP 库、领域特定提示、自我精炼,以及把 LLM 生成的代码转换为可在搜索期间被利用的、与 EvoSuite 兼容的测试组件。

一个值得注意的、由 LLM 引导的混合 SBST 方法是 CodaMosa(Lemieux 等,2023),它也使用 LLM 在搜索期间逃离局部最优。虽然我们的方法共享这一动机,它在类级引入了自我精炼提示策略,而 CodaMosa 在方法级运作。此外,我们采用 DynaMOSA(Panichella 等,2018a),先前研究已表明它优于 MOSA(Panichella 等,2015)(Panichella 等,2018a;Campos 等,2018;Lukasczyk 等,2023)。CodaMosa 也针对通用类,而非 NLP 库这类领域特定软件。由于没有可用的 CodaMosa Java 实现,我们依据已发表的设计重新实现了它。如第 5.1 节所示,在 NLP 组件上,LLMSuite 的分支覆盖率高出 11%,行覆盖率高出 8%,变异得分高出 11%。

9. 结论

对领域特定软件(如 NLP 库)而言,自动化单元测试生成仍然特别具有挑战:测试输入必须满足语义(例如领域特定知识)、句法与结构(例如输入数据格式)约束。在本文中,我们提出 LLMSuite,一种混合框架,把大语言模型(具体为 ChatGPT-4o-latest-2025-07-12)集成进 EvoSuite 中基于搜索的测试生成方法。当没有任何搜索目标在连续多代中得到改进(搜索停滞)时,LLMSuite 调用 LLM,依据语义(例如 JavaDoc 与注释中所表达的内容)以及被测类的结构,用自我精炼提示策略生成类级测试套件。这些 LLM 生成的测试被注入 EvoSuite 正在演化的种群。

在对五个广泛使用的 Java NLP 库中 100 个类的评估中,相对 EvoSuite,LLMSuite 的分支与行覆盖率提高 15%,变异得分提高 5%;相对仅使用 LLM 的基线,结构覆盖率高出 36%,变异得分高出约 24.7 个百分点;相对我们对 CodaMOSA 的 Java 重新实现,分支与行覆盖率分别高出 10% 与 8%,变异得分增益为 11%(RQ1)。在考察设计替代方案时(RQ2),我们发现 LLMSuite 的默认配置一致优于使用不同 LLM、替代提示策略、更少提示或仅改进字符串的变体——这表明类级提示、自我精炼以及与 SBST 的紧密集成十分重要。我们的定性分析(RQ3)表明,当需要有意义的输入、正确的 API 调用序列或领域特定的配置设置时,LLMSuite 特别有效;而对以数值计算或算法逻辑为主的类,优势有限。最后,在考察它与人工编写测试的交互时(RQ4),我们发现 LLMSuite 通过执行那些测试未到达的行为与配置来补充人工测试套件,而把二者结合会通过若干反复出现的互补行为形式带来额外覆盖。

我们指出若干未来工作方向。首先,一个重要扩展是把 LLMSuite 与使用编译器或执行反馈进行迭代自我修复的、基于智能体的 LLM 系统进行比较。虽然此类方法很有前景,我们的框架被设计为使我们能够在 EvoSuite 内部完全控制整个生成、解析、修复与搜索流水线。尽管如此,与外部智能体基线的直接比较将是有价值的未来工作。其次,未来工作应使用其他开放权重与闭源 LLM 评估 LLMSuite。特别是,我们计划调查其他具有不同能力与对齐策略的通用模型的影响。因为该框架与模型无关,这些模型可以在不改变总体架构的情况下纳入,从而在不断演进的 LLM 图景中更广泛地评估通用性。我们还计划试验面向代码的 LLM,例如 StarCoder 与 DeepSeek Coder。此外,研究把 LLM 生成的断言更直接地集成进流水线,能否改进超出 EvoSuite 回归断言的行为验证,也将是有趣的。最后,我们计划在结构化输入与领域特定语义给测试生成带来挑战的其他领域中研究 LLMSuite,例如科学计算库、语法分析器与编译器。

致谢。 本研究部分由荷兰科学研究组织 NWO 通过 Vici“TestShift”资助(编号 VI.C.182.032)资助。

署名与许可

本文译自 Amirhossein Deljouyi、Annibale Panichella、Andy Zaidman(代尔夫特理工大学,荷兰代尔夫特)的论文 Enhancing Automated Unit Test Generation for NLP Libraries Using Large Language Models(arXiv:2609.14784)。原文以 CC BY 4.0 许可发布。译者:智测团队。

Found it useful? Pass it on

WeChat

Scan with WeChat to open it on your phone and forward it.

Subscribe via RSS

Submit a correction