资讯动态

AI生成测试用例重复率高?从提示词约束到语义相似度的去重实践

发布时间:2026/9/9 4:04:52 来源:尧图企业网站定制
如果你也用AI批量生成测试用例多半会遇到一个很尴尬的问题AI确实能在几分钟内给你吐出一大批用例但里面总觉得“差不太多”。核心功能A的用例生成了三份只是换了几种说法同一个校验逻辑既能叫“用户名为空提示”又能叫“未输入用户名时做校验”甚至还有一条“用户名留空时给出错误提示”。你辛辛苦苦去重比写用例还累最后反而怀疑用AI是不是划算。我在实际项目里也踩过这个坑后来花了很大精力专门梳理“AI生成测试用例的重复问题”。这里想做一个系统性的总结为什么AI老生成重复用例、重复会造成哪些隐性成本、以及从生成前到生成后如何建立一套可落地的去重流水线。内容会更偏向功能测试用例和测试平台侧的去重实践适合QA、测试开发、AI应用开发者参考尤其是正在搭AI生成用例工具、又不想被重复数据淹没的团队。下面直接进正题。1. AI生成的测试用例为什么总在“同一条路上反复横跳”想解决去重不能只盯结果得先理解模型是怎么把用例“编”出来的。只要你不了解重复的来源哪怕加再多的相似度算法也只是在补救永远跟不上模型制造重复的速度。1.1 大模型生成机制本身就是重复的来源大语言模型本质是一个“根据上文预测下一个词”的概率系统。当你给它一句“生成登录功能测试用例”它每次输出都是一次独立采样。采样过程中模型不知道自己刚才在上一条里已经把“空密码校验”写过了它只知道“现在要输出一条看起来合理的用例”。于是同一条规则换个措辞再生成一次对它来说没有任何负担。我之前做登录模块批量生成实验让模型生成20条登录页面用例结果出现了三组高度近似的组合“密码为空时提示请输入密码”“密码栏未填写时校验拦截”“空密码提交显示请输入登录密码”。这三条从功能设计文档角度看基本就是同一个校验点。但在模型眼里它们是三次不同的概率采样自然就当作不同内容输出。这还只是最表层的重复。更深一层是模型的“归纳能力”会用错地方。比如你要求“覆盖所有异常场景”模型会下意识把一个边界条件拆成很多语言变体它认为“空”“为空”“未填写”“留空”都值得单独写一条其实在产品规则里这些都是同一个等价类。模型并不真正理解等价类边界和测试设计方法它只是把常见的测试表述凑在一起凑得越多看起来越专业重复也就越严重。1.2 重复用例到底会造成什么实际损失很多人觉得用例重复顶多“不好看”删掉就行。等用例量一上来你就会发现重复的成本远不止阅读体验差。第一是执行和评审成本。测试用例评审时同一规则出现三条近似用例评审会要来回确认是否真的重复、保留哪条、合并哪些步骤。一个模块还好如果整个系统都这么搞评审效率直接减半。第二是自动化脚本维护的隐形负担。进入自动化阶段后重复用例意味着两份脚本做同一件事业务规则一旦调整你得同时改两处甚至三处少改一处就会出现“有的脚本执行通过、有的执行失败”的假异常排查半天才发现原来是同一规则的重复用例。第三是覆盖率统计失真。你本来覆盖率已经达标但因为同一场景被拆成多条冗余用例分母虚高真正的业务场景覆盖反而看不清。我见过一个团队AI生成了上千条用例覆盖率数字很好看但核心异常流程缺失就是因为生成的内容大量集中在同一个规则上反复打转。所以去重这事不只是“清理数据”它直接关系到测试资产能不能被信任。1.3 先明确一个边界什么才算“重复”做去重前必须先定义“重复”否则后面的一切算法都没有基准。我的经验里与其看文案不如看四个维度被测对象、前置条件、操作动作、核心预期。如果四条都一致只是措辞不同判定为重复。比如“点击登录按钮后不输入密码系统提示密码不能为空”和“密码框留空点击登录页面出现密码必填校验”被测对象都是登录按钮前置条件都是密码为空操作动作都是点击登录核心预期都是拦截这就算重复。但有一条要特别注意同一条边界规则下的不同参数值不能算重复。比如登录密码长度限制密码为空、密码为1位、密码为17位这三条预期的提示可能一样但不能简单判重因为它们是边界值的不同档位对应不同测试数据。这类用例更适合合并成一条数据驱动用例把参数放到测试数据集里而不是生成三条“长得像”的独立用例。去重算法如果没想清楚这一点很容易把边界值全部杀掉。2. 去重不能靠单一算法要从生成前到生成后分三层设防最开始我以为这类问题只要写个脚本做相似度计算就能解决后来发现不对。如果生成源头不做约束AI每分钟生成几十条后置去重再强也只是“事后补救”而且误判率会很高。真正靠谱的方案是把去重前移到生成环节形成生成前、生成中、生成后三层防线。2.1 生成前约束把“已有用例清单”和“测试点清单”喂给模型生成前约束的核心原则就是一句话别让模型在信息真空里写用例。你如果只给它一个“请为订单模块生成用例”的指令它就只能靠训练记忆里的通用套路自由发挥当然容易把行业常见写法都堆一遍。我在实践中会先在提示词阶段要求模型输出一份“测试点清单”。具体做法是让它按“功能模块-业务规则-校验点”的层级先列出所有测试点不直接给完整用例。然后告诉它这份清单必须覆盖需求文档里明确的正常流程和异常拦截数量不要超过30个。模型先把测试点列出来后再人工或者用规则脚本过一次把明显同一规则的测试点合并最后才让模型基于这份“已经过一轮筛选的测试点清单”逐条展开成完整用例。同样重要的一件事是把已有用例的标题或摘要传给模型。AI不是人类你不会在写新用例时翻一下历史用例库模型更不会。如果我们期望它不要覆盖已有内容就得主动把“不要重复”的信息喂到上下文里。实际操作时我会把当前功能模块下历史用例的规范化标题拼成一段放进提示词并加一句“以下为已存在的用例请勿生成与这些用例核心步骤和预期一致的用例”。这个方法简单直接但因为上下文长度有限更适合在单模块内做而不是把整个用例库一股脑塞进去。2.2 生成中约束半结构化输出和按模块分桶生成前约束只能减少一部分重复因为模型太容易在长文本生成过程中“忘记”自己的输出。这时候需要把输出格式从自然语言改成严格的半结构字段。我们内部用的生成格式大致是模块订单创建标题订单金额超过单笔限额时拦截前置条件已登录用户账户余额充足操作步骤进入订单创建页面输入超过单笔限额的金额点击提交预期结果系统提示金额超出限额订单不提交这个格式看着简单但力量在于强约束。如果让模型自由写步骤它会把“输入订单金额”和“提交订单”混成一段话将来做去重就没法拆。而结构化输出后步骤是一个清晰的列表我们后续可以抽取“操作链”用于判重比对。字段一旦固定模型就没机会把两个不同测试点塞到一条用例的长文本里文本之间的相似度分布也会更好处理。另一个实用技巧是按模块分桶生成。让一个大Prompt生成100条用例肯定会互相重复。我把生成任务切得非常碎一次只生成一个接口或一个规则集合的用例例如“请只针对登录页面的用户名输入规则生成用例不要涉及密码规则”。这样每批用例内部聚焦批与批之间的边界也清晰去重范围从全库缩小到模块内效果会好很多。2.3 生成后拦截精确指纹优先语义相似度兜底不管生成前和生成中怎么设防始终会有漏网之鱼。所以生成后必须做一次系统级的拦截一般走两条路精确指纹去重和语义相似度去重。精确指纹去重听起来技术味很重其实原理很简单先把用例文本做规范化处理比如去空格、统一大小写、把同义词换成标准说法然后对这段文本计算一个MD5指纹。如果库里已经存在相同指纹说明这条用例跟已有用例在文本层面完全一致直接拦截。但文本层面完全一致的重复毕竟是少数更常见的是“换个说法”的重复。这时就需要语义相似度计算。思路是对用例文本做向量化然后计算新用例和历史用例之间的余弦相似度超过一定阈值就标为疑似重复。这块放到下面一节详细展开因为参数阈值和工程落地才是真正容易出问题的地方。3. 从指纹判重到语义判重一套可以直接落地的实现方案理论聊再多不如给出能直接跑的步骤。这一节我会按实际操作顺序来从文本清洗开始到指纹计算再到相似度阈值选择尽量把实现细节说清楚。3.1 先把用例文本洗干净再做判断去重算法处理的是文本但测试用例不像普通文档它里面有大量“噪声”会让算法误判。最常见的问题是中文符号差异比如“密码为空系统提示”和“密码为空,系统提示”一个是中文冒号一个是英文逗号计算机看来是不同字符串人类看其实完全一样。所以任何去重实现的第一步都是清洗。清洗至少要处理这几类问题统一全角半角标点把中文标点转成英文或统一去掉去掉文本首尾空格和多余空白符英文一律转小写把操作词收拢成标准表达比如“填写”“键入”“输入”“录入”都归一到“输入”“提交”和“点击提交”按场景映射为“点击提交”去掉用例标题里的编号前缀比如“TC_001”“1.”这类无意义噪声。清洗规则做得越细后面的精确指纹才越有价值。如果你跳过这一步直接做MD5一条用例换个冒号就能绕过判重那整个精确指纹层形同虚设。3.2 结构化指纹去重的代码思路清洗完之后就可以计算精确指纹了。我给出一个最基础的代码思路实际使用时你可以把业务里常用的词和格式规范加进去。import hashlib import re def clean_text(text: str) - str: # 去掉首尾空格和常见符号 text text.strip() # 统一全角逗号、冒号为半角 text text.replace(, ,).replace(, :) # 去除标点符号和多余空格 text re.sub(r[\s], , text) text re.sub(r[。、,.!;:()\[\]], , text) # 英文转小写 text text.lower() # 同义词归一化 text text.replace(填写, 输入).replace(键入, 输入) text text.replace(点击, 单击).replace(校验, 验证) return text def build_fingerprint(case: dict) - str: raw f{case[module]}|{case[title]}|{-.join(case[steps])}|{case[expected]} cleaned clean_text(raw) return hashlib.md5(cleaned.encode(utf-8)).hexdigest()这个函数会把一条用例的模块、标题、步骤、预期结果拼成一个字符串清洗后计算MD5。如果两条用例的指纹一样说明它们在这些核心维度上完全一致。实际部署时我会把指纹字段存在用例表里并加唯一索引新增用例前先查指纹是否存在存在就直接拒绝。这个方案成本极低适合做第一层硬拦截。有一点要注意步骤列表的拼接顺序不能随便调整。很多相似度算法喜欢把词袋化处理排序后比较但测试用例的步骤顺序是业务逻辑不能乱动。比如“先登录再下单”和“先下单再登录”在很多业务里是两个完全不同的场景一旦排序就会误判。所以指纹拼接必须保留原始顺序。3.3 语义相似度去重的阈值怎么定才靠谱精确指纹只能拦一模一样的情况真正的拦路虎是“换一种说法”的重复这时需要向量相似度。常见路线有两种一种是用TF-IDF或SimHash做文本向量另一种是用BERT系列的中文Embedding模型做语义向量。前者实现简单、速度快但只能捕捉词面重合对同义词改写基本无效。后者能识别“请输入密码”和“密码不能为空”这类语义相近的文本但会引入模型依赖和一定的计算成本。我的建议是先用Embedding模型生成向量再算余弦相似度。现在中文Embedding生态已经很成熟单条用例过模型也就几十毫秒几千条用例全量比对完全可以接受。向量化之后最重要的问题是阈值取多少。这个阈值没有标准答案不同业务文本风格差异极大。我能提供的是标定方法先人工从历史数据里挑200到300对用例标注它们是否重复要求至少覆盖明显重复、语义相近但不算重复、完全不重复三种情况。然后对这些成对文本计算相似度画出分布图。一般来说你会看到两个峰一个集中在0.9以上基本都是改写重复另一个集中在0.65到0.85可能是语义相近但边界不同。阈值选在两个峰之间的低谷处通常会在0.86到0.92之间。我实际项目里会把疑似重复分成两档相似度高于0.92的自动进入“待确认重复列表”相似度在0.8到0.92之间的提示“人工复核”不自动删除。这样做的原因是高阈值区间误杀率很低但低阈值区间如果自动处理很容易把边界值误杀损失大于收益。宁可让一部分重复进入人工队列也比让算法把有效用例删掉强。4. 落地过程中最常见的坑与排查思路算法本身不复杂真正的坑往往出现在你以为“逻辑没问题”的时候。下面这些案例都是我实际踩过的每个都对应一类真实场景。4.1 把“参数不同”误判成重复去重粒度搞错了有一次我们去重脚本上线后自动拦截了一批用例当时看上去没问题结果后续人工复核时发现误杀率很高。印象最深的是一条“密码长度为16位时提交成功”和“密码长度为17位时提示超出最大长度”被标成了疑似重复。从文本角度看两条用例的步骤几乎一样打开登录页输入密码点击登录然后断言页面提示。Embedding模型给它们的相似度高达0.9因为除了一两个数字不同之外整体结构完全一样。但作为测试设计它们覆盖的是同一个限制条件的两侧边界是需要保留的。后来我们调整了策略不能只看步骤和标题的相似度还要把“参数值”单独抽出来作为特征加入判断。如果两条用例步骤相同但参数值是紧邻的边界值就不进入去重候选。更通用的做法是让模型在生成时把“测试数据”和“测试步骤”分离数据像参数化表格一样单独存放判重只针对“步骤模板”。这样既保住了数据驱动用例的完整性又不会让它们因为长得像而被误删。4.2 步骤顺序不能排序别让算法教坏业务另一个坑发生在相似度计算的预处理阶段。试用SimHash时有同事觉得“文本分词后先排序再哈希”能提高召回率因为他看到很多重复用例的步骤顺序并不完全一样。这个想法初看合理但放到业务里就出问题了。“下单后支付”和“支付后下单”是两个流程不是同一场景的顺序变体。测试用例的步骤是一个有向动作链不能当成普通文本的词袋处理。排序去重适合那些“提示文案相同”的场景比如各种输入框的空值校验步骤顺序确实不影响“提示不能为空”这一结论。但一旦涉及业务流程必须保留步骤原始顺序否则算法会给业务开倒车。我们的做法是只有同模块且操作动作属于“校验类”时才允许做顺序无关的相似度比较其他类型一律保留顺序参与计算。4.3 向量相似度高不代表一定重复别让算法直接删用例这是我见过最危险的自动化思路相似度超过阈值就直接把新用例删掉。曾有一个AI生成测试用例平台内部为了控制用例库膨胀把自动去重阈值设到0.85每周能拦截几百条“重复用例”。团队一开始很满意直到一次发版前评审发现所有关于“订单并发时库存扣减”的用例都被拦截了因为它们的措辞和“订单重复提交”用例高度相似但实际验证的是两个完全不同的并发逻辑。数据不会直接说明业务意图向量相似度只是从文本层面告诉你“这两句话长得像”但两条长得像的用例可能因为前置条件不同、验证点不同而对应完全不同的业务风险。所以我把去重系统设计成两级自动拒绝的只有精确指纹命中的结果语义相似度标出的疑似重复一律进入人工确认池再配合二次评审决定保留还是合并。准确率再高的模型也不能替测试人员做业务决策它只能帮测试人员缩小审查范围。4.4 常见问题排查速查表现象可能原因处理建议新用例和旧用例完全一致还是被加入指纹计算前没有做全角半角、空格清洗统一清洗后再生成指纹并加唯一索引边界值用例被误判为重复去重粒度是完整用例没有区分测试数据与步骤模板将数据参数抽离只对步骤模板判重相似度很高但业务场景不同向量只吃文本不感知前置条件差异把前置条件和核心验证点单独加入特征流程类用例被排序处理导致误判预处理阶段对步骤做了全排序保留步骤原始顺序只对校验类做顺序无关比较新用例越来越多全库两两比对越来越慢全量暴力计算复杂度高按模块分桶或引入向量索引如faiss、milvus同一批生成的用例重复率特别高提示词没有提供现有用例模型在“真空”中输出生成前注入历史用例标题和测试点清单5. 把去重变成持续迭代的反馈闭环去重工具上线后很多人以为这事就结束了其实没有。AI生成用例的重复模式会随着提示词、模型版本、需求文档风格变化而变化。一套固定算法跑半年准度很快会下降必须把它设计成一个能持续从人工评审结果中学习的反馈闭环。5.1 存量清洗与增量拦截要分开做我建议把去重拆成两条线。一条是存量清洗专门处理已经堆在库里的大量历史冗余数据。存量清洗的动作不要一次性做完先用高阈值挑出“高度疑似重复”的条目交给业务人或QA确认后合并。一次清洗的数据量控制在几百条以内确认一批清理一批避免误删。另一条是增量拦截也就是每次AI生成新用例后走一遍提示词约束、指纹去重、语义相似度筛查的流程。增量侧的目标不是追求“零重复入库”那样容易把有效用例误杀而是把重复率控制在一个能接受的范围比如从最初的20%以上压到5%以下。定一个真实的KPI去重工作才不会变成没完没了的“救火”。5.2 把确认后的重复样本回灌给提示词策略反馈闭环里最重要的一步是把人工确认过的重复样本变成下一轮生成的“禁令”。我们在系统里会维护一个“已确认重复规则库”每条记录包含一个标准测试点描述和若干种重复写法。每次构造AI生成提示词时把当前模块的重复规则库插入上下文明确告诉模型“这些说法已经存在不要再生成类似内容”。这一步本质上是在给模型喂更高质量的few-shot负例效果比单纯在结果端拦截更明显。实际用过一段时间后你会发现AI生成测试用例的重复率不是靠一个算法、一次清理就能一劳永逸的它需要从生成前到生成后持续往下压。我个人在项目里体会最深的一点是去重的收益不能光看拦截了多少条还要看有没有误杀有效覆盖率。宁可让少量重复用例混进人工复核池也别让一个模型或算法替测试团队做业务判断题。这样把去重系统当成持续迭代的测试资产来运营AI生成用例才能真正从“数量多”变成“质量高”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价