Industry & PracticeResearch & Benchmarks
改 动 之后 界面 哪里 变 了: 2026 年 的 变更 感知 GUI 回归
RippleGUItester 从一次代码改动出发,用历史场景知识生成界面测试,在改动前后两个构建上执行,再对照截图判断差异是预期还是缺陷。在 Firefox、Zettlr、JabRef 和 Godot 的 111 个合并拉取请求上,表 1 给出 119 个去重缺陷、精确率 46.4%;其中 26 个仍在最新版本。单个拉取请求平均 54.8 分钟、5.99 美元。

In this piece
本文是 arXiv 论文 RippleGUItester(arXiv:2603.03121)正文第 1 节至第 7 节的中文译文,由智测团队翻译。参考文献未逐条展开。正文保留了论文报告的缺陷数量、精确率、漏报原因和开销。
1 引言
现代软件靠频繁的代码改动持续演进。加功能、修缺陷、做可维护性整理都要改代码,改动也可能引入新缺陷。即便有大量测试和代码评审,仍有不可忽略的一部分改动会带进新缺陷(Mostafa 等,2017;Kamei 等,2012;Kim 等,2008;Śliwerski 等,2005;Yin 等,2011;Wen 等,2019),既伤害使用体验,也抬高调试和维修成本。
为看清这个现象,作者分析了代码改动以及它们引入的缺陷。即便测试很严,被分析的改动里仍有 12.2% 引入了新缺陷。图 1 是 Firefox 的一个代表例子。修复 Issue 1858633(密码页打开时 URL 片段不正确)时,触发条件是:网站开着,再从应用菜单打开密码页。这次修复无意中引入了另一个缺陷 Issue 1937085。新缺陷的触发方式是从弹出窗口进入密码页,结果密码页开在弹出窗口里,而不是主浏览器窗口。
这个例子集中了检测「改动引入的缺陷」时的几个难点。
(i)触发事件序列很多样。同一个页面,可以从应用菜单进,也可以从弹出窗口进。
(ii)测试数据不好构造。例如需要一个能弹出登录窗的网站(论文举例为 https://x.com/)。
(iii)界面行为的测试预言很难事先写死。比如密码页到底该开在主窗口还是弹出窗口。
缺陷也可能来自跨场景的副作用:修一个使用场景,弄坏另一个看起来无关的场景。例如修复设置页缺少滚动条(Issue 1792881),无意中导致没有搜索结果时搜索框发生位移(Issue 1793730)。两件事都在设置页,但一个是滚动,一个是搜索,测试时很难提前想到。
因此很多缺陷会逃过现有流程。回归测试和持续集成大体被限制在预定义的执行路径和预期行为上。探索式测试(Kaner,2008;Cem Kaner 与 Bach,2006;Whittaker,2009)善于发现意外问题,但它不由代码改动驱动,改完之后该探索什么,缺少系统指引。这个缺口要求一种测试:明确由代码改动驱动,同时又能探索这次改动更广的影响。
本文提出 RippleGUItester。它是一套由改动驱动的测试系统,目标是通过界面,系统探索一次代码改动的涟漪效应,找出这次改动引起的、用户能看见的问题。它不只盯着作者打算改的那一点,而是去发现难以事先料到、现有测试常常漏掉的间接影响和跨场景影响。
系统有三个部分:测试场景生成器、测试场景执行器、缺陷检测器。
生成器从给定的代码改动出发,用大模型做变更影响分析,找出可能受影响的行为,生成初始测试场景。这些场景再用从历史 Issue 和拉取请求里挖出的场景知识来加厚,补上多样的触发事件序列。为了能真正执行,再把抽象的测试数据实例化,得到现实、可执行的测试场景。
执行器把每个场景分别跑在改动前和改动后两个构建上。
检测器做差分分析。它找出并标注两个构建截图之间的视觉差异,再结合所执行的场景和这次改动的意图,判断差异是预期的行为变化,还是非预期的缺陷。
评测把 RippleGUItester 用到四个复杂、广泛使用的系统上的数百个真实代码改动:Firefox、Zettlr、JabRef 和 Godot。结果显示,它能发现现有测试套件、持续集成和代码评审都没注意到的、由改动引入的缺陷。合计发现 26 个此前未知、并且在被评测系统的最新版本里仍然存在的缺陷。报告之后,16 个已修复,2 个已确认,6 个仍在讨论,2 个被判定为预期行为。开销相对高:平均每个拉取请求 54.8 分钟、5.99 美元。但它能找出很难自动发现的真实界面回归。
贡献有四条。
第一,观察并分析真实系统里的代码改动及其引入的缺陷,量化普遍程度,并指出这类缺陷难测的实际原因。
第二,提出 RippleGUItester。据作者所称,这是第一个变更感知的界面测试系统,通过系统探索涟漪效应,发现代码改动引起的、用户可见的问题。
第三,在四个广泛使用的系统、数百个真实改动上评测,发现 26 个仍在最新版本中的未知缺陷:16 个已修复,2 个已确认,6 个讨论中,2 个被标为符合预期。
第四,公开数据集和实现,供后续研究使用(见论文的数据可用性说明)。
2 动机
无论是做任务、加功能、修缺陷还是重构,代码改动都可能无意引入新缺陷。作者考察这种事有多常见,以及实践里为什么难预料、难检测。全文把 Issue 用作任何任务、功能、缺陷或重构的统称,把 Pull Request(拉取请求)用作为处理某个 Issue 而做的代码改动。
2.1 引入缺陷的改动有多常见
作者分析了 2019 年 5 月 1 日至 2025 年 1 月 1 日合并进 Firefox 的 97,347 个拉取请求。选 Firefox,是因为它有完整的缺陷管理系统 Bugzilla,能把拉取请求和它所解决的 Issue、以及它无意引入的缺陷连起来。尽管合并前有 lint、单元测试、回归测试和代码评审,仍有 11,910 个拉取请求引入了新缺陷,占全部改动的 12.2%。这是保守估计:有的缺陷从未被发现,有的被发现了但没有连回引入它的拉取请求。这些缺陷通常由用户、测试人员或开发者在后续版本里发现。这既影响使用,也带来可观的测试、调试和修复成本。
2.2 检测改动引入的缺陷为什么难
这些缺陷不是简单回归,重跑现有回归测试靠不住。它们常常要复杂的触发步骤,预言也很难事先写出来。
2.2.1 难构造的触发步骤
触发步骤有两部分:事件序列和测试数据。事件序列是把软件带到特定状态的用户动作或系统事件。测试数据是用来检验正确性、性能和可靠性的输入。主要难点是复杂系统里事件序列和测试数据的多样性。
事件序列多样。图 1(a) 中,Issue 1858633(密码页打开时 URL 片段不正确)的触发是:网站开着,从应用菜单打开密码页。修复它的拉取请求引入了 Issue 1937085:about:logins 开在登录弹出窗口里,而不是 Firefox 主窗口。图 1(b) 的触发序列是:打开一个带弹出登录窗的网站(例如 https://x.com/),点「Sign in」激活弹出窗,在输入框上右键,选择「Manage Logins」。所以在改动阶段测缺陷,必须考虑多样的事件序列。图 1 只画出了最后一步事件。
测试数据多样。Issue 1750072 为密码输入框加入内置的显示/隐藏控件。测这个增强,事件序列是一样的:打开含密码框的界面,观察这个控件。不同的含密码框界面就是不同的测试数据。这类界面太多,穷尽测试不现实。结果漏掉了 Thunderbird 的邮件账户设置页(Issue 1913889:两个切换密码可见性的按钮),以及保存密码的 doorhanger(Issue 1936548:多余的「显示密码」选项)。因此,要充分验证改动,必须生成有代表性、又多样的测试数据。
跨场景副作用。解决某个具体 Issue 的拉取请求,可能影响乍看无关的其他使用场景。修复 Issue 1792881(设置页缺少垂直滚动条)时,无意造成 Issue 1793730:没有搜索结果时,搜索框和页面内容发生位移。两者都在设置界面,但对应不同交互:滚动设置页,以及做一次没有结果的搜索。测滚动条修复时,很难预料还要测搜索场景。这就是看似无关的使用场景之间,隐藏依赖很难被揭开。
2.2.2 难预测的测试预言
只触发不够,还要有预言才能发现缺陷表现出来的样子。上面的例子说明,预言很不规则,也很难预测。修错误的密码页 URL,导致密码页开进弹出窗口;启用内置的显示/隐藏控件,出现多余的「显示密码」;补上垂直滚动条,搜索条意外位移。基于规则的预言做不到。能不能抓住这种不规则、难预测的预言,是另一个主要难点。
合在一起:引入的缺陷往往要多样的事件序列、不同的测试数据、以及多个使用场景之间的交互才能触发,表现又依赖难预测的预言。这解释了为什么很多缺陷能逃过现有测试。
3 方法
图 2 是总体结构。图标区分三类:大模型(机器人)、算法(齿轮)、混合(握手)。
RippleGUItester 把多模态大模型的自然语言理解、代码理解和视觉能力,与特定应用的知识结合起来,捕捉代码改动引入的缺陷。三个组件分别简称为生成器、执行器和检测器。生成器分析给定改动,找出可能受影响的使用场景,并生成可执行测试场景(第 3.1 节)。执行器在被测系统的改动前、改动后两个构建上运行这些场景(第 3.2 节)。检测器比较两次执行,判断观察到的差异是预期行为变化还是非预期缺陷(第 3.3 节)。
3.1 测试场景生成器
生成器分析改动的潜在影响,并纳入应用特有知识来生成场景。如第 2.2 节所说,改动引入的缺陷常常伴随难构造的事件序列、多样的测试数据和跨场景副作用。为此生成器有三个子部分:测试场景生成、事件序列增强、测试数据增强。
给定一个拉取请求,测试场景生成用大模型做变更影响分析,识别可能受影响的使用场景,产出初始场景。事件序列增强再通过有目标的查询,取回更多应用知识,用来加厚这些场景的事件序列。测试数据增强识别所需数据和约束,生成具体数据实例,得到带真实测试数据、可执行的场景。
3.1.1 输入准备
为了让场景既对齐预期改动、又能暴露非预期缺陷,每个拉取请求的输入是改动意图和代码改动,如图 3。
改动意图。收集拉取请求描述,以及它明确解决的 Issue(若有),包括摘要和详细描述。
代码改动。捕捉这次拉取请求的具体实现。用实现层描述(例如提交说明)理解改动如何落到代码里,并抽出细粒度信息:被修改的文件、路径和补丁。
先前的改动意图。第 2.2 节指出,改动可能带来跨场景副作用。作者利用仓库里的历史追溯来缓解。对拉取请求里每一行被修改或删除的代码,用代码—提交追溯(例如 GitHub blame)找出上一次改过同一代码区域的提交,再取回对应的拉取请求和它们解决的 Issue。这些历史上相关的拉取请求和 Issue 的摘要与描述,被当作先前的改动意图,因为眼下这块代码当初就是为满足那些意图而被引入或修改的。按与当前拉取请求的代码重叠程度排序,重叠越高,越可能受影响。这样既能验证当前拉取请求的预期行为,也能防止先前已经处理过的场景发生回归。
例如第 2.2 节里,修复 Issue 1792881(滚动条)的拉取请求,与先前为支持搜索条而改过的代码有重叠。于是相关的搜索 Issue 和拉取请求被识别为先前意图,促使系统生成的场景同时检查滚动条修复,以及搜索条行为是否仍正确。
3.1.2 测试场景生成
生成场景要把自然语言的改动意图和对应的代码改动放在一起分析,代码还可能跨多种语言。传统变更影响分析(Ren 等,2004;Ryder 与 Tip,2001)在这里不够,因为它们不支持多语言。作者因此用大模型做场景生成,利用它对自然语言和多语言代码的理解。
初始化。用系统级角色提示把模型初始化成「根据改动意图和代码改动生成测试场景」的工具。提示有三块:角色指定;任务描述,给出分步指令;输出规范,规定响应格式,包括「变更影响分析」和「测试场景」等字段。
输入就是改动意图及其代码改动。
影响分析与场景生成。模型先分析这次改动的理由和期望结果,再看实现该意图的具体代码修改。在此基础上做变更影响分析,识别可能受影响的最终用户场景,包括高风险或敏感情形。然后从影响分析导出测试场景,既覆盖新增或被修改的行为,也覆盖关键的既有流程。所有场景严格从最终用户视角来写,不包含代码、内部实现和开发术语。
以修复 Issue 1858633 的拉取请求为例。场景生成认定:这次改动影响的是从应用菜单以及其他入口打开密码页的方式。系统据此生成演练该行为的场景。图 4(a) 是其中之一:从内部 about: 页面打开密码页时,确认没有被追加 URL 片段。
3.1.3 事件序列增强
初始场景出来之后,再加厚事件序列,提高多样性。
场景知识库。第 2.2 节指出,触发缺陷常常需要多样的事件序列。历史缺陷报告是众包来的使用场景,适合做事件序列增强。例如,历史报告里打开密码页的路径有很多:首选项页、自动完成下拉的页脚、页面信息对话框、保存/更新 doorhanger。这些很难手工枚举。但并非每份报告都有有意义的使用场景。作者先用规则过滤日志型报告(时间戳过多,或带有 intermittent 一类关键词),再用大模型进一步挑出描述最终用户界面交互的报告。这些报告构成场景知识库(Scenario Knowledge Base,SKB)。为了检索,报告被切成固定词元长度、相互重叠的块,嵌入向量空间,同时支持关键词和语义检索。生成测试时,从知识库取出相关事件序列,用检索增强生成加进场景。
初始化时,模型的角色是:根据变更影响分析,用多样的事件序列增强测试场景。输入是场景生成阶段产出的改动意图解释、变更影响分析和测试场景。这些输入用来检索相关的事件序列知识。
检索与增强分四步。先分析影响分析和初始场景,找出可以提高事件序列多样性的机会。再生成有目标的查询,从知识库里的历史最终用户用法和测试场景中检索替代事件序列。然后把检索到的序列整合进场景。最后校验增强后的场景仍然简单、自包含、彼此独立。如果增强把场景弄得过复杂,就改成新生成场景,而不是硬塞进原场景。场景仍然只从最终用户视角书写。
沿用图 4(a)。影响分析表明该拉取请求影响从不同入口打开密码页,事件序列增强就去知识库里找更多进入方式。图 4(b) 是增强后的场景:不从应用菜单进,而从弹出窗口和页面信息界面打开密码页。从弹出窗口打开密码页,正是触发 Issue 1937085 的关键事件。这一步的场景仍含抽象事件(例如「从某网站打开弹出窗口」),还没有具体测试数据(例如指定一个稳定会弹出窗口的网站)。
3.1.4 测试数据增强
事件序列丰富之后,再识别并实例化所需测试数据,使场景可执行。
模型的角色是识别并实例化每个场景需要的测试数据。输入是已经带有多样事件序列的场景。
对每个场景,模型分析场景,推断每个数据项的约束,再实例化成具体、可执行的值。已有示例若存在,就按约束校验,不合规则替换;否则生成现实世界中的数据。为提高覆盖,引入有代表性、但不冗余的变体,包括边界情况,以及有意义时互相对照的合法与非法输入。跨场景时,同一数据类型使用不同的代表性例子,以尽量多样。实例化后的数据写回场景,并再次校验场景简单、自包含、独立。过复杂就另起新场景。仍然不写内部实现和开发术语。
沿用图 4(b)。测试数据增强把抽象数据需求变成具体输入,合成一段能稳定造出弹出窗口上下文的脚本。如图 4(c),这份具体数据使场景能够执行,并触发只在经由弹出窗口访问密码页时才出现的 Issue 1937085。
3.2 测试场景执行器
如图 2,执行器在被测系统的改动前、改动后两个构建上执行生成的场景。它有两部分。(1)基于大模型的界面指令翻译:根据当前界面状态,把高层场景步骤译成结构化界面指令。(2)算法式的界面指令执行:在容器环境里执行这些指令。循环直到场景执行完,或达到预设的执行预算。
3.2.1 界面指令翻译
用系统级角色提示初始化,并维持一个记忆会话,记录已经执行过的界面指令。对每个场景步骤,把高层描述译成结构化指令,再调用执行组件在容器里执行。定义了十一种标准界面动作:单击、右键、长按、双击、三击、输入、滚动、拖拽、移动、按键、等待。部分动作需要参数:单击、右键、长按、双击、三击和移动需要位置;输入需要位置和文本;滚动需要方向;按键需要键值。
输入是带有多样事件序列和测试数据的场景,加上被测系统当前界面状态。界面状态以视觉形式给出(例如截图),用来指导翻译。
翻译时,模型结合场景和当前截图决定下一步。若该步的前置条件未满足,先生成指令去满足前置条件,再做主步骤;前置动作也当作普通步骤。然后产出结构化指令。指令包含:动作(例如单击)、目标控件的名称和坐标、若是输入则包含文本、若是按键则包含键、若是滚动则包含方向。没有时序依赖的多条指令会打进同一次响应,以提高效率。例如保存登录时,若网站、用户名、密码和保存按钮都已可见,就在一次响应里生成填写各字段并点击保存的指令。
3.2.2 界面指令执行
结构化指令生成后,翻译组件调用执行组件,在被测系统上执行。执行组件用命令行界面工具(例如 xdotool)做单击、输入、滚动等动作。执行后重新截图,记录更新后的界面,供后续步骤使用。
全部测试在隔离的 Docker 环境里跑,以保证环境一致。对每个被测系统,先构建基础镜像,装入所需构建工具、运行时依赖和环境配置。对每个待测拉取请求,从基础镜像派生镜像,在其中同时构建改动前和改动后两个版本,得到对应可执行文件。每个场景对每个构建都在全新容器实例里执行,容器启动的是对应版本的可执行文件。这样避免残留文件、缓存状态或先前执行留下的配置干扰。为了公平、一致地比较,改动后构建上生成的界面指令,被原样重放到改动前构建上。
沿用图 4(c)。执行器在两个构建上都跑这个测试。改动前,密码页开在主窗口(图 5(a));改动后,开在弹出窗口(图 5(b)),从而暴露 Issue 1937085。两次执行的事件序列不同,但暴露的是同一个底层缺陷。
3.3 缺陷检测器
场景在改动前后都执行完之后,检测器分析界面状态,识别非预期的行为变化。三个部分:(1)基于像素的算法式界面差异检测与标注;(2)基于大模型的缺陷检测;(3)基于大模型的缺陷过滤。
3.3.1 基于像素的差异检测与标注
先对改动前后对应截图像素级比较,找出差异。用阈值滤掉细小变化(本文设为 30),再做形态学膨胀,把邻近的差异像素连起来。然后提取连通分量,找出成片的变化区域,并为每个区域生成包围盒。结构化差异信息是一组字典,每项有索引和该区域的包围盒坐标。最后把这些区域以带编号的包围盒画到截图上。结构化差异信息和标注后的截图,都作为后续缺陷检测的输入。
3.3.2 缺陷检测
用系统级角色提示,让模型解释改动前后的界面差异,识别这次代码改动引入的非预期问题。提示把改动意图里没有明确描述的界面差异视为潜在缺陷,同时排除瞬时伪影,例如渲染未完成,以及步骤执行失败或延迟造成的界面不同步。检测步骤之间用对话会话保持上下文。
输入包括改动意图,以及由测试场景生成阶段产出的改动意图解释(说明代码改动背后的理由)。另外还输入已执行的测试场景。场景里每条已执行的界面指令提供四项:(1)所执行的指令;(2)改动前的界面截图;(3)改动后的界面截图;(4)Parsed Info,即两张截图之间已检测差异的结构化表示。每处差异有索引,并用包围盒定位(x1, y1, x2, y2),对应编号的包围盒叠在截图上。
检测分几步。先做整体界面分析,概括可见界面状态,枚举在场组件。再遍历 Parsed Info 及其视觉标注,做差分分析,描述具体变化:元素增减、位置或尺寸偏移、内容或属性更新(例如标签和取值)。每处差异分成两类:与改动意图及其解释一致的,标为预期;超出指定范围的非预期偏离,标为缺陷。除了逐条分类,还评估这些变化对整体界面的一致性和影响,检查布局错位、多余或缺失的元素、不一致或冲突的状态与行为、破裂或令人困惑的交互,以及可能的可用性或无障碍退化。对每个独特的非预期问题,给出简短推理,并提交缺陷报告。不做臆测,只报告清晰、可观察的缺陷。图 6 是修复 Issue 1858633 时的缺陷检测示意。
3.3.3 缺陷过滤
每个待测拉取请求会执行多个场景以尽量提高覆盖。同一个底层缺陷可能在不同场景里反复触发,或在单个场景里触发多次。缺陷检测也可能因为截图里的瞬时渲染伪影产生虚假报告。因此原始输出里会有重复或无效报告。作者用一个基于大模型的后处理步骤,在给出最终结果前过滤报告。
重复过滤。两份报告若描述同一根因,即使用词不同,也视为重复,只保留一个代表。
渲染时序伪影过滤。截图可能在界面完全稳定前就被截下。瞬时视觉差异,例如缺少插入符、控件只渲染了一部分、布局暂时不正确,会被误报。这些要滤掉。
非确定界面行为过滤。去掉由不稳定或非确定界面行为引起的报告,包括布局偏移、界面元素随机排序、多次执行中面板选中不一致。复杂界面应用里这些很常见,并不表示功能缺陷。
沿用图 5。检测器先做像素差异检测。因为两幅界面全局不同,整个界面被一个紫色包围盒包住(索引为 0)。再结合改动意图分析这处差异,如图 6。综合视觉证据和自然语言意图后,它判定:观察到的行为差异超出了这次改动的预期范围。这次改动只针对 URL 片段处理和预选机制。于是检测器生成一份描述该非预期行为的缺陷报告。
4 实验
评测要回答四个研究问题。
RQ1:发现此前未知缺陷的效果如何?
RQ2:报出的缺陷精确率是多少?
RQ3:对已知回归缺陷的召回如何?
RQ4:计算和时间、金钱成本是多少?
4.1 实验设置
4.1.1 实现
实现大约 13,400 行 Python,使用常规 Python 库做界面自动化、图像处理和向量检索,并用官方 SDK 调用大模型服务。被测系统的构建和场景执行都在 Docker 容器内,以保证一致和可复现。实验在三台硬件不同但可比的机器上进行:(1)MacBook Pro,Apple M4 Pro,24 GB 内存,macOS 26.2;(2)MacBook Pro,2.0 GHz 四核 Intel Core i5,16 GB 内存,macOS 14.1.2;(3)Linux 服务器,Intel Core i9-14900(24 核 32 线程,最高 5.8 GHz),64 GB 内存,Ubuntu 24.04.1。三台机器上结果一致,作者据此认为方法不依赖特定硬件。
模型选用当时的前沿模型,用来探索这项任务在模型能力上的上限。生成器和检测器用 GPT-5.2,理由是推理和多模态能力强。执行器用 Claude Opus 4.5,理由是在把指令落到界面元素上、定位控件方面更好。辅助任务用 GPT-5 Nano 在构建场景知识库时过滤历史缺陷报告,利用它做轻量分类的效率;用 text-embedding-3-large 把知识库编成向量供检索。
4.1.2 被测软件
四个广泛使用的开源桌面应用:Firefox,偏重速度和隐私的网页浏览器;Zettlr,一体化的出版工作台;JabRef,管理 BibTeX 和 BibLaTeX 数据库的 Java 图形应用;Godot,跨平台的二维和三维游戏引擎。选择依据是多样性、流行程度和可追溯性。领域不同,便于看泛化。都足够流行:截至论文写作时,Firefox 超过 1.1 万 GitHub star、6,302 名贡献者;Zettlr 超过 1.24 万 star、151 名贡献者;JabRef 超过 4,200 star、874 名贡献者;Godot 超过 10.5 万 star、3,165 名贡献者。每个项目至少有八年开发史,并且持续有开发者参与。都有公开的 Issue 跟踪和版本控制历史,因此能追溯代码改动和已解决的 Issue。
4.1.3 数据集
场景知识库:爬取 246,665 条 Firefox 缺陷,过滤后保留 74,625 条。Zettlr 爬取编号 1 到 6,035 的全部 Issue 和拉取请求,过滤后保留 2,832 条。JabRef 爬取编号 1 到 14,554,知识库中保留 3,256 条。Godot 爬取编号 1 到 113,618,过滤后保留 35,657 条。为防止数据泄漏,为某个待测拉取请求检索知识库时,只使用创建时间早于该拉取请求的 Issue 和拉取请求。
Firefox 在代码改动和它所引入的缺陷之间有明确追溯,适合评测对已知回归的召回。从 Firefox 随机选取 30 个已合并拉取请求,每个至少关联一个被引入的缺陷。Zettlr、Godot 和 JabRef 没有同样稳定的细粒度追溯,评测重点是在真实开发设置下发现此前未知的缺陷。Zettlr 和 Godot 各收集 2026 年 1 月之前最近合并的 50 个拉取请求。JabRef 起初也收集 50 个,但滤掉非功能性或不适合测试的之后数量不够,因此把窗口放宽,取 2026 年 1 月之前最近合并的 80 个。过滤后保留:Zettlr 38 个,Godot 25 个,JabRef 18 个。
表 1 汇总缺陷检测结果。列依次为被测系统、拉取请求数、缺陷数、真阳性数、假阳性数、精确率。
| 系统 | 拉取请求 | 缺陷数 | 真阳性 | 假阳性 | 精确率 |
|---|---|---|---|---|---|
| Firefox | 30 | 29 | 52 | 41 | 0.559 |
| Zettlr | 38 | 63 | 67 | 95 | 0.414 |
| JabRef | 18 | 17 | 19 | 21 | 0.475 |
| Godot | 25 | 10 | 10 | 14 | 0.417 |
| 合计 | 111 | 119 | 148 | 171 | 0.464 |
论文说明:表 1 的缺陷数是去重后的未知缺陷;真阳性计数包含重复。Firefox 的缺陷数还不计入已知的地面真值缺陷。各系统真阳性相加为 148,假阳性相加为 171,148 / (148 + 171) = 46.4%,与合计精确率 0.464 一致。正文 RQ2 有一处写成「总体检出 111 个真阳性、精确率 46.4%」。111 与合计行的拉取请求数相同,与各系统真阳性之和以及 46.4% 的算法不一致。下文分项数字以表 1 为准。
4.2 发现未知缺陷的效果(RQ1)
作者人工检查所有被测拉取请求上报出的缺陷,判断是否为此前未知。如表 1,RippleGUItester 检出 119 个独特缺陷。其中 26 个在项目最新版本里仍然存在。向对应开发团队报告后,16 个已修复,2 个已确认,6 个仍在讨论,2 个被标为符合预期。
几个具体例子。JabRef 的 PR 14489 改进了 HTTP 服务器代码,但引入一个缺陷:HTTP 服务器的 endpoint/libraries/demo 失败,返回带 HTML 的 HTTP 500,演示库获取被破坏(Issue 14807)。报告后很快被修复。这个例子说明,工具也能发现后端服务器逻辑和执行中的问题。
Zettlr 的 PR 5976 重构了 Markdown 脚注的解析和渲染。工具分析潜在影响,生成聚焦高风险行为的场景,例如多块脚注。执行后发现多个缺陷:多块脚注渲染不完整、文字间距异常、缩进续行处理错误、脚注标记缺失、无效脚注仍出现工具提示(Issue 6099、6102、6103、6104、6106)。图 7 展示了其中一部分,对比改动前和改动后构建。报告后这些问题都被修复,说明对非平凡重构引入的缺陷也有效。
Godot 的 PR 113611 修复编辑器工具提示里键盘快捷键缺少「+」分隔符,乍看纯属外观。变更影响分析标出潜在的本地化风险,依据是先前改动意图:PR 106943 和 PR 106946 改过重叠的、处理工具提示翻译的代码。据此生成并执行语言切换条件下的工具提示渲染场景,发现 Issue 114157:编辑器切到法语后,快捷键工具提示变成混合语言。开发者追溯后认为原因是翻译暂时过期,而不是功能代码错误。工具仍然发现了用户可见的问题,并促使翻译及时更新。
Godot 上检出的缺陷相对少。细看主要是 Godot 的测试特点:很多缺陷要先构造一个最小复现工程才能触发。构造这种工程的准备步骤不轻,场景经常在完成前就用完预设的执行预算。缓解办法可以是提高执行预算上限,或者复用已有资源。很多 Godot 的拉取请求和 Issue 已经附带最小复现工程,把它们纳入场景知识,或用别的机制复用,能明显提高 Godot 上场景执行的效率和效果。
4.3 报出缺陷的精确率(RQ2)
作者把所有被测系统上报出的问题人工标为真阳性或假阳性。按表 1 的分项:Firefox 上 30 个拉取请求检出 52 个真阳性(55.9%);Zettlr 上 38 个拉取请求检出 67 个真阳性(41.4%);JabRef 上 18 个拉取请求检出 19 个真阳性(47.5%);Godot 上 25 个拉取请求检出 10 个真阳性(41.7%)。工具能在不同项目里发现未知缺陷,但假阳性也不少。171 个假阳性分成以下几类。
4.3.1 界面渲染与时间不稳定(78/171,45.6%)
来自非确定的界面渲染,以及因为用截图代表界面状态而带来的截图时机波动。
截图时机或渲染延迟,39 例。截图时刻会影响瞬时视觉元素,例如文本插入符、只渲染了一部分的控件、延迟出现的动态内容。这些良性差异被误报为缺陷。
界面布局或渲染不稳定,39 例。同样的动作在不同次执行里视觉排列不同。这在 Zettlr 和 JabRef 里常见。例如 Zettlr 打开文件时,文档可能出现在不同的分栏窗口;JabRef 里侧栏组件的相对顺序,例如 Web Search 和 Groups 面板,可能每次不同。
可能的缓解:把同一组界面指令执行多次,检查界面状态是否一致,滤掉瞬时和非确定的视觉差异。
4.3.2 大模型推理与幻觉(47/171,27.5%)
来自模型推理和视觉落地的局限。
误解或幻觉式的界面渲染,38 例。有些假阳性来自检测阶段如何在截图上标注像素差异。标注区域可能对应预期的界面变化,但模型把它理解成缺陷。例如一次改动有意把「Clean up entries」对话框里的 Apply 按钮挪了位置。改动后界面是对的,检测却把按钮的原位置标成差异区域,在改动后截图上画出一个空的占位框。模型把这个标注区域理解成按钮缺失或变成空白,误报为缺陷。另一些是纯粹幻觉:模型报告两边截图里都不存在的控件,例如多出来的编辑图标。
假定执行了并未执行的动作,6 例。模型根据没有发生的动作推断界面状态。例如它报告输入框缺少焦点指示,但焦点动作根本没执行。
不必要的改进建议,3 例。模型把预期行为当成缺陷。例如在没有发起重命名时,Zettlr 里 Rename 按钮处于禁用是预期的,模型仍把它报成问题。
缓解:用提示工程讲清视觉标注的语义,避免模型把检测标记当成界面元素。对错误动作推断引起的幻觉,可以用带执行感知的数据微调,让执行轨迹和视觉推理更一致。
4.3.3 界面交互与重放不稳定(26/171,15.2%)
来自非确定的界面交互,以及交互重放时的不一致。
控件定位或交互失败,15 例。控件定位错了,或计划中的交互没有按计划执行,于是实际交互偏离场景,产生的界面状态被误报。例如模型报告 Ctrl+A 再按 Backspace 没能清空地址栏,检查后发现地址栏从未被正确聚焦。另一个例子:用拖拽改变对话框大小失败,因为执行实现只指定拖拽起点,终点用默认值。
界面差异导致重放对不齐,11 例。代码引入的合法界面变化,使重放的交互错位。在改动后构建上有效的坐标,到改动前构建上可能对不准正确控件。例如改动调整了密码输入框的位置。在改动后构建上,交互正确点到挪走后的密码框;同一交互重放到改动前构建时,因为布局不同,没有点到对应字段。结果改动后的执行触发了弹出窗口,改动前没有。
缓解:提高界面执行的稳健性(例如给出完整拖拽轨迹),用执行感知的微调改进元素定位,并把大模型放进重放阶段,以便发现并适应交互失败。
4.3.4 由代码改动带来的预期界面差异(17/171,9.9%)
检测出的视觉差异正确反映了拉取请求引入的行为或界面状态变化,但仍被标成缺陷。例如改动后构建里 Commit 按钮是禁用(变灰)的,改动前是启用的。这是更新后的校验逻辑的预期结果:在特定条件满足前禁用按钮。
缓解仍是提示工程。检测提示要更明确地区分非预期缺陷和合法的界面变化。
其余 3/171(1.8%)。包括:改动前后都存在的既有缺陷;领域知识不足、难以归类的模糊情形;以及一次重放不稳定。后者是场景执行生成了文件名随机的 PDF。重放时试图复用播放阶段生成的文件名以保持一致,但重放生成了另一个文件名,对应文件不存在,于是重放时文件访问失败,造成假阳性。
4.4 对已知回归缺陷的召回(RQ3)
召回只在 Firefox 上做,利用已知地面真值,看 RippleGUItester 能发现多少已知缺陷。如表 1,测完 30 个拉取请求后检出 52 个真阳性,其中 10 个是重复。去重后剩 42 个独特真阳性。再与 Firefox 问题跟踪系统里记录的地面真值比较。30 个拉取请求各自至少引入一个缺陷,地面真值合计 54 个。其中 6 个主要由日志或堆栈描述(例如 Issue 1923374),很难精确推断对应的错误界面行为,评测时无法可靠判断工具检出的缺陷是否就是这些以日志描述的地面真值,因此排除。另外排除 2 个被标为重复的缺陷。最终用于评测的地面真值是 46 个。如图 8,工具检出的独特缺陷中有 13 个与地面真值重叠,33 个地面真值没有被检出。图 8 左侧是工具的独特真阳性,右侧是独特的地面真值。
4.4.1 漏掉的地面真值
原因分两类:场景生成覆盖不足,以及场景执行不完整。
场景生成覆盖不足,20/33(60.6%)。生成的场景没有覆盖揭示地面真值所需的关键触发步骤。这些缺陷的触发步骤往往难预测,或条件很严,生成时难以预料。
难预测的触发步骤,15/20。修复 Issue 1745734(深色主题下,HTTP 网站登录表单上的不安全连接图标几乎看不见)的拉取请求引入了 Issue 1935402:单击并拖动「此连接不安全」面板,会导致其内容不可见。这种触发是不常见的交互(单击并拖动一个面板),场景很难覆盖。可能的改进是在场景生成里加入随机化,例如注入随机交互动作,提高不可预测性和多样性。
严格的触发步骤。原文标为 5/15。修复 Issue 1592682 的拉取请求引入 Issue 1725969:当「用户名」排序选项处于激活时,点击保存后「创建新登录」模式没有被关闭。生成的场景演练了排序,也在多种前置条件下演练了创建新登录,但没有覆盖「用户名排序激活的同时创建新登录」这个特定组合,于是漏掉。为控制实验成本,每个拉取请求生成的场景上限是 7 个。放宽上限、允许更充分的场景生成,有可能减少这类漏报。15/20 与这 5 例合计,对应覆盖不足的 20 例;原文把严格触发的分母写成 15,与 20 并不对齐。
场景执行不完整,13/33(39.4%)。场景里已经包含所需触发步骤,但因为执行不完整或不正确而漏掉。
达到最大执行预算,4/13。被引入的 Issue 1934682 需要很长的前置条件。生成场景的执行在触发步骤发生前就达到预算,缺陷被漏掉。预算有两条:(1)最大大模型交互次数,限制场景执行期间的模型调用次数;(2)最大界面指令数,限制执行的界面交互总数。实验里分别设为 20 次模型交互和 35 条界面指令。提高预算可能缓解。
执行不正确,9/13。五种失败模式使场景无法正确完成。
(i)触发步骤执行错误,4 例。特定控件上要求的交互(例如密码侧栏)被错误地在别的界面上下文里执行(例如密码页),触发步骤没有被正确演练。
(ii)执行时元素定位错误,2 例。执行组件点到错误坐标(例如在错误位置点击「创建新登录」),执行卡住。
(iii)改动前或改动后构建失败,1 例。部分场景因此没有执行。
(iv)界面交互实现不正确,1 例。某些交互(例如拖拽)需要多组坐标来指定轨迹,当前实现只提供一组坐标,导致执行失败。
(v)重放失败,1 例。场景执行时生成随机文件名。重放时同一组界面指令试图访问这些文件,因随机命名已不存在,重放失败。
可能的改进:构建更完整的场景知识图谱,用细粒度交互步骤刻画用户任务,从而更好指导执行。对构建失败、交互实现不正确和重放失败,可以采用更稳健的工程办法,例如把大模型放进重放过程,做自适应控制和错误恢复。
4.5 计算与金钱成本(RQ4)
图 9 给出开销分解。平均值是每个拉取请求 5.99 美元、54.8 分钟。
场景知识库是一次性构建。Zettlr 用 3.22 小时、0.50 美元。JabRef 用 5.12 小时、0.73 美元。Godot 用 46.21 小时、5.43 美元。Firefox 用 50.28 小时、20.82 美元。
跨全部被测系统,测试每个拉取请求平均 54.8 分钟、5.99 美元。如图 9:生成器的时间和成本都相对低,占全部执行时间的 7.5%、全部成本的 4.3%,平均每个拉取请求 4.10 分钟、0.26 美元。执行器是主要开销,占全部时间的 44.4%、全部成本的 62.0%,平均 24.3 分钟、3.72 美元。检测器占全部执行时间的 48.1%、全部成本的 33.7%,平均 26.4 分钟、2.02 美元。
开销高,主要来自执行器和检测器。两阶段都要处理图像输入,以便推理当前界面、生成界面交互指令,并判断行为差异是预期还是非预期。评测对象是桌面应用,截图分辨率高,模型要处理的图像词元多,计算成本就高。实现里使用前沿多模态模型(GPT-5.2 和 Claude Opus 4.5),是为了在较强模型能力下评测方法,避免较弱模型把方法本身的效果混淆掉,并给出当前大模型下可达到的性能上限。
单个拉取请求的时间和费用看起来不低,但 RippleGUItester 分析的是代码改动,要找出对应拉取请求引入的系统级缺陷。人工做同样的分析,仍然要开发者投入不小的精力、时间和人力。它补充的是现有人工测试和代码评审,而不是替换。即便在 Firefox 这种成熟、评审严格的项目里,它仍能找出现有测试和评审漏掉的缺陷。在拉取请求阶段发现缺陷,可以阻止有问题的代码被合并,从而提高质量和用户满意度。而且检出的缺陷直接连到具体代码改动,修复成本更低,开发者不必做大量调试和定位。
5 效度威胁
主要威胁是 RippleGUItester 报出的缺陷里有假阳性。为缓解,作者深入分析了假阳性的主要来源,并讨论了未来减少假阳性的具体改进方向(见第 4.3 节)。
评测在四个开源、带界面的桌面应用上进行,不能代表所有软件。缓解方式是从不同领域选取(网页浏览器、文档编辑器、文献管理器、游戏引擎),并且代码库大、仍在积极维护。RippleGUItester 并不针对某个应用或框架定制,因此作者认为可以适配到其他带界面的系统。
另一项威胁是大模型和计算资源的选择。作者用前沿模型探索可达到性能的上限,换更小或能力更弱的模型,结果可能不同。这个选择是为了评估基于涟漪效应的界面测试是否可行、潜力多大,而不把方法局限和模型约束混在一起。未来工作可以换其他模型,看成本与效果的权衡。
6 相关工作
探索式测试。系统测试层面上,探索式测试已被证明有效(Kaner 等,1999;Cem Kaner 与 Bach,2006;Itkonen 等,2012;以及若干后续工作)。已有不少实施和管理探索式测试的原则与指南。也有工具支持。Tapir(Bures 等,2018)生成导航型测试用例以减少重复。基于场景的探索式测试,也称肥皂剧测试(Buwalda,2004),设计复杂、真实的使用场景,去激发难以事先预料的失败。Su 等(2022、2023、2024)用系统知识图谱刻画用户任务和失败,再组合相关行为来生成测试场景,以支持肥皂剧测试。SoapOperaTG(Su 等,2023)进一步优化这类测试的生成和选择。Su 等(2025)用接近人的创造力和智能,自动执行这些探索式场景并做缺陷检测。这些工作在场景生成、执行和缺陷检测上能力强,但它们不感知代码改动,不利用「眼下正在引入的具体改动」这一信息。本文则明确由一次代码改动触发,系统探索可能受影响的场景,以发现这次改动引入的缺陷。
变更影响分析。变更影响分析被广泛用于推理代码修改的效果(Law 与 Rothermel,2003;Ren 等,2004;Ryder 与 Tip,2001;等)。Ryder 与 Tip(2001)刻画面向对象程序中的变更影响,指出继承和动态分派等语言特性会造成非局部效应。Ren 等提出 Chianti(2004),一个面向 Java 的实用变更影响分析工具,通过分析版本之间的代码级依赖,识别受影响的回归测试。这些方法在程序和测试层面有效,但大体限于单一编程语言,依赖静态或动态代码分析,不推理最终用户的界面行为。本文同样由改动驱动,但与语言无关,并且在用户可见的界面交互这一层探索代码改动的涟漪效应。
变更感知的回归测试。本文也与回归测试选择和优先级排序相关(Elbaum 等,2002;Harrold 等,2002;Yoo 与 Harman,2012),但根本差别是:本文为给定的代码改动生成新测试,而不是只从旧测试里挑选。近期工作用代码改动或拉取请求指导回归测试和验证(Zhou 等,2026;Gröninger 等,2025;Pradel,2026)。ChaCo(Zhou 等,2026)做最后一公里的回归测试增强:识别拉取请求里的代码改动,选择或增强已有测试,提高对被改代码的覆盖。它停在代码和测试层,目标是提高特定改动的回归充分性,不推理改动引起的界面层效果。ChangeGuard(Gröninger 等,2025)用学习引导的执行来验证代码改动,对改动前后的程序状态做成对比较。它能检测行为差异,但主要针对程序级执行,不显式建模最终用户的交互场景或界面行为。Testora(Pradel,2026)利用拉取请求关联的自然语言信息,把生成测试所暴露的行为差异分成预期或非预期。Testora 关注单一语言代码库里的 API 级回归检测,不考虑界面交互。与这些工作不同,本文面向以界面为中心的软件,把一次代码改动当作多个用户可见场景上涟漪效应的震中,从而发现在代码或 API 层很难预料的间接缺陷和跨场景缺陷。
场景知识挖掘。已有工作从缺陷报告或 Issue 里挖掘执行信息来复现缺陷,例如把复现步骤翻译成测试用例(Fazzini 等,2018;Zhao 等,2019;Sidong 与 Chunyang,2024),或从缺陷视频重建执行轨迹(Bernal-Cárdenas 等,2020;Havranek 等,2021)。也有研究从缺陷报告抽取结构化信息来支持探索式测试。BUGINE(Tan 与 Li,2020;Li 与 Tan,2020)跨应用推荐相关缺陷,辅助开发者测试。这些工作说明众包产物有价值,但它们没有用场景知识去加厚「测试代码改动」时的事件序列。
7 结论
尽管有大量测试和代码评审,代码改动仍会引入缺陷。作者对真实系统的分析表明,有不可忽略的一部分缺陷仍会逃过现有测试,原因是事件序列复杂、测试数据要求高、跨场景副作用,以及测试预言难预测。
RippleGUItester 是一套由改动驱动的探索式界面测试系统。它做三件事。(i)通过对场景知识库的检索增强生成,加上基于大模型的变更影响分析,生成并加厚针对代码改动的测试场景。(ii)在改动前和改动后两个构建上执行这些场景。(iii)用多模态差分分析检测非预期的行为差异。
在真实系统上的评测表明它在实践中有效,发现了 26 个此前未知、且在最新版本中仍然存在的缺陷。这些结果说明,在真实开发流程里,变更感知的探索式界面测试有实际价值。作者还分析了观察到的局限,包括假阳性的主要来源和漏掉的缺陷,并给出具体的缓解策略和后续改进方向。更广义地说,通过把代码改动明确连到可观察的用户行为,RippleGUItester 为测试界面日益动态、持续演化的交互系统打下了基础。
Found it useful? Pass it on
Scan with WeChat to open it on your phone and forward it.