☰
AI自动化识别需求文档生成测试用例:原理、落地与避坑指南
2026/10/10 12:40:31 网站建设 项目流程

简介:这是一款面向测试人员、产品经理与业务分析人员的 Windows 桌面工具,用于将 PRD、自然语言需求、Office/PDF 文档及图片需求自动转换为功能测试用例,可显著缩短需求理解与用例编写时间,并减少遗漏场景。资源为 Req2TestCase 的 1 份 docx 操作手册,整体约 7.4MB,从安装启动、模型配置、需求导入与本地 OCR,到需求点预分析、长文档自动分段生成、用例合并去重与编号重排、覆盖矩阵、质量检查、历史版本对比及 Markdown/CSV/Excel/JSON/禅道 CSV 导出均有详细说明,并附常见问题处理建议。该工具支持 DeepSeek、豆包、千问、智谱 GLM 及自定义 OpenAI 兼容接口,可自行配置 API Key,并支持本地 OCR 识别文档内嵌图片与扫描型 PDF,适合需要在本地快速搭建 AI 用例生成环境的测试团队。目前已有 179 人学习下载,可作为测试提效、用例评审与回归覆盖的实用参考。

1. 这套工具到底在解决什么:文档还没评审完,用例已经有人替你先写了

做过测试的同学都有这种体验:需求评审会开到一半,产品念完一段功能描述,测试大脑里已经开始画流程图、想边界值了。等到文档定稿排期下来,花在“读懂需求、拆业务规则、设计用例结构”上的时间,往往比真正敲字写作的时间多得多。这个“AI自动化识别需求文档生成测试用例工具”解决的正是前两步——从任意一份需求文档里抽取业务逻辑,把它整理成可执行的测试用例。它不是一个能写出神级用例的“测试专家”,而是一个帮你把文档里的规则搬进用例模板里的“读文档机器人”,能读懂多少、拆得多准,取决于你怎么喂它、怎么约束它。

这套工具适合三种人:被长篇 PRD 逼疯的功能测试工程师、需要把历史需求批量转换成用例资产的团队、以及想评估“AI 辅助测试设计”到底能做到什么程度的负责人。接下来我不会讲理论框架,直接按“原理 → 选型 → 落地 → 排错 → 进阶”这条链路往下走,每一步都给出能抄的配置和命令。

2. 先搞懂原理:AI 是根据什么把一段需求“识别”成测试点的

很多人拿到这类工具第一反应是“把 PDF 传上去,等结果”。但实际操作中,识别质量的高低,核心不在模型本身,而在你给模型看的输入结构和输出约束。这一节把需求和 AI 之间的那条链路拆开讲清楚。

2.1 需求文档的三种常见形态与各自应优先采用的解析方式

“识别需求文档”的前提是先把文档变成模型能读的文本。不同形态的文档,对应的解析策略完全不同。常见做法是先把文档分类,再决定走哪条解析管线。

第一种是结构化文档,比如接口文档、数据库字典、带固定表格的 PRD。这些文档里最有价值的信息在表格里:字段名、类型、必填项、边界值。解析这类文档,优先用表格抽取工具把每个表格转成 Markdown 或 CSV,再连同其前后解释性文字一起交给模型。不要把整份 PDF 丢进去让模型自己找表格,模型在处理长文档时对表格中列与列的对应关系很容易出错。

第二种是半结构化文档,比如带标题层级、列表、但存在大段啰嗦描述的功能说明文档。这类文档适合先把标题层级拆开,按章节切片输入模型。关键点是:不要按页切片,要按“语义块”切片。一个语义块是“一个功能点及其相关描述”。如果一页同时包含三个功能点,按页切会让模型把三个功能点混在一起;如果按章节切,一个章节下可能包含多个功能点,模型又容易漏掉后面的。这里有个玄学问题:切得越细,单次生成质量越稳,但总成本和耗时越高,需要根据文档篇幅找到平衡。

第三种是非结构化文档,比如聊天记录整理的需求、手写拍照件、临时会议纪要。这类文档没有固定结构,但往往包含最多的隐性业务规则。我一般先做全文抽取,让模型把文档里出现的“动词 + 名词 + 条件”全部列出,构建出一份行为清单,再依据清单生成用例。这个过程相当于先做一次信息压缩,把散落在自然语言里的规则显式化,再进行后续的用例设计。

2.2 信息抽取不是“让 AI 读一遍”,是给 AI 一张 Schema

很多团队用 AI 生成测试用例效果差,根因是交互方式还停留在“把需求贴进去,问一句‘请帮我写测试用例’”。这种开放式提问,模型会按它见过的互联网语料中的模板来生成,产出五花八门,覆盖度完全不可控。更可靠的做法是定义一张信息抽取的 Schema——也就是一张期望输出的结构表,让模型像填表一样填写信息,再基于填好的结构去生成用例。

我常用的 Schema 包含这几个字段:功能模块名、行为主体、操作动作、前置条件、输入数据、预期输出、异常分支描述。把这段 Schema 放进系统提示词里,要求模型先输出“需求结构化结果”,再做“测试用例生成”。这一步的价值在于:把模型的黑匣子打开了一半,你可以直接检查它有没有理解错需求。如果“操作动作”是“查询用户列表,并按创建时间倒序排列”,说明模型读对了;如果它写成了“点击用户列表”,说明它漏了排序规则,源头就修正,比生成用例之后再返工省力得多。

2.3 为什么要保留上下文:从单条需求到业务规则的闭环

另一个常见误区是把每一条需求单独抽取、单独生成,最后把用例拼在一起。这样做的代价是丢失跨需求的上下文。

举个例子:一段需求说“用户点击删除按钮,弹出确认框,确认后删除该条记录”,另一段需求说“用户点击删除按钮,若该记录已被关联引用,则提示不可删除”。两条需求放在一起,才构成完整的状态流转。如果分开生成,第二条的“不可删除”分支会被忽略,生成出来的用例只有正向路径和取消路径,缺少核心异常分支。

所以,我的解析链路在进入 AI 之前,会先把同一模块下的所有章节合并成一个“上下文窗口”,让模型在统一的上下文里完成抽取。这个做法对 token 消耗有压力,但对用例质量的价值极大。实际项目中,上下文带来的准确性提升远大于多消耗的 token 成本,这笔账值得算。

3. 落地选型与提示词设计:把需求文本切碎再重组

原理讲清楚了,接下来是选型和具体搭建。测试团队不一定都有算法背景,所以我会从“能用更快落地”的角度来讲,而不是追求 SOTA 效果。

3.1 模型与解析链路选型:本地和云端如何取舍

在做这套工具时,先明确一个大前提:不建议自己从零训练模型。信息抽取和用例生成属于语义理解与结构化输出任务,用通用大语言模型处理已经足够,真正需要定制的是任务流程和数据切片方式。

选型上有两条路线。一条是全云端路线:把需求文档上传到某个在线平台,上传后由云端的文档解析模型抽取文本,再由通用模型生成用例。这种方式上手快,但文档内容和测试用例都会经过外部服务,对涉及敏感业务的团队来说需要谨慎评估合规性。另一条是半本地路线:本地完成文档解析、文本清洗、切片;再把切片文本发送给模型接口;回收结果后在本地做结构化校验和用例归档。这种路线的好处是需求文本不用全量外发,并且可以在解析阶段做更细的控制,包括排除页眉页脚、图表序号等干扰信息。

我一般采用半本地路线,把模型调用封装成一个独立的服务模块。这样即使后续换模型供应商,也只需要改接口配置,不需要动整个链路。同时,本地解析出来的中间结果会以 JSON 形式落盘,方便随时做抽检——这一步在纯云端方案里很难做细。

3.2 提示词模板:信息抽取阶段的系统提示词

下面给出我实际在用的信息抽取提示词模板,核心思想是“先抽取,再生成”,且强制要求模型按 JSON 输出。先看代码:

system_prompt = """ 你是一个需求分析助手。我会给你一段需求文档片段。 请你完成以下两步任务: 第一步,抽取信息,按如下 JSON Schema 输出: { "module": "功能模块名", "behavior_subject": "行为主体,即谁在执行操作", "operations": ["操作动作列表,按执行顺序排列"], "preconditions": ["前置条件列表"], "input_data": [{"字段名": "", "类型": "", "约束": ""}], "expected_output": "描述操作完成后期望出现的系统反馈或数据变化", "exception_branches": ["异常分支描述,列举尽量全面"] } 第二步,根据上面的 JSON,生成测试用例。 输出 JSON 数组,每个元素格式如下: { "case_title": "用例标题", "priority": "P0/P1/P2", "preconditions": "前置条件", "steps": ["操作步骤,每步一句话"], "expected": "预期结果" } 约束: 1. 测试用例必须覆盖 exception_branches 中的每个分支。 2. 每个 operation 至少对应一条用例。 3. 不要编造需求文档中未提及的字段或规则。 """

这段消息传入模型后,你会拿到两份结构化内容。抽取结果和用例结果分开的好处是:当用例质量不佳时,你可以先检查抽取结果,而不是直接怀疑模型能力。如果抽取阶段就漏了异常分支,那用例必然不全;如果抽取正确但用例不对,问题出在生成阶段,可以考虑更换模型或调整温度参数。

import json from openai import OpenAI client = OpenAI( base_url="http://localhost:11434/v1", # 本地推理服务的 OpenAI 兼容接口 api_key="not-needed" ) def extract_and_generate(doc_chunk: str): response = client.chat.completions.create( model="qwen2.5:14b", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": doc_chunk} ], temperature=0.2, # 低温度保证输出的稳定性 response_format={"type": "json_object"} ) return json.loads(response.choices[0].message.content)

几个参数的思考路径。temperature是这套任务里最重要的参数之一。信息抽取任务本质上是“阅读理解”,温度过高会让模型输出不同措辞但语义相近的描述,甚至出现幻觉字段;我一般固定0.2,并在后续人工审核阶段只关心漏与错,不关心表述差异。response_format开启后,模型会尽可能保证输出是合法 JSON,但这不代表 Schema 完全符合你定义的结构,下游仍要做字段校验。

3.3 输出约束:用例模板与字段校验规则

只让模型输出 JSON 还不足以进入用例管理库。从实际使用角度来说,测试平台和用例管理工具通常要求固定字段,比如用例编号、所属模块、级别、预置条件、操作步骤、预期结果。因此拿到模型返回后,还要做一次字段映射和校验。

常见的做法是让模型输出不包含编号,编号由工程侧生成。原因很简单:模型的编号不稳定,两次调用可能从 1 开始,也可能从 0 开始,还会出现跳号。而工程侧生成编号可以做到与前端展示、筛选条件保持一致。另一个要做的校验是“步骤数校验”。生成用例最低要求是“两步一步骤”,即至少有一个操作步骤和一个预期结果。如果模型输出的steps是空数组或者expected为空,这条用例直接丢弃并记录日志,便于追溯是哪段需求导致的。

def validate_cases(cases: list): valid = [] for case in cases: if not isinstance(case.get("steps"), list) or len(case["steps"]) == 0: continue if not case.get("expected") or len(case["expected"]) < 2: continue case["case_id"] = generate_case_id(case["module"]) valid.append(case) return valid

这里的generate_case_id可以做成带模块前缀的递增器,例如MOD_A_001。在工程落地时,我更推荐把校验结果输出一份报告,包含丢弃率、各模块用例数量、异常分支覆盖率等指标。这些指标能帮你判断当前使用的模型和提示词组合适不适合这套任务,也为后续切换模型提供对照依据。

4. 最小可跑通 Demo:挑一段需求文本走完整个流程

理论说得再多,不如跑一段真实的输入输出。这一节用一段围绕 VR 眼镜核心系统的需求描述作为样本,从文本切片开始,到最终输出结构化用例结束。你可以复制这段代码,改造成你自己的流程。

4.1 准备测试样本:一段真实风格的半结构化需求

我用一段简化后的需求描述作为示例。注意这段文本故意保留了多个功能点、前置条件和异常分支,目的是展示模型在复杂上下文下的表现。

# 测试文档片段 【提醒功能】 用户在系统内创建提醒后,可在提醒列表页面查看所有提醒。 点击某条提醒进入详情页,可编辑提醒时间、提醒内容和重复频率。 提醒触发时,系统应推送通知给用户;如果用户未开启系统通知权限, 则应通过应用内消息告知用户。 提醒到期的前 10 分钟,如果用户正在使用该应用,应在首页展示浮层提示。 用户最多可以创建 50 条提醒,达到上限时,点击“新建提醒”按钮应弹窗提示。

把这段文本保存为requirement_sample.md,接下来写脚本读取并做语义切片。切片的粒度按行为主体和功能编号拆,这里可以简单按“【】”标题切。

4.2 端到端生成脚本:从文件到用例 JSON 一次完成

下面这段代码把文件读取、切片、调用模型、校验和落盘串起来,形成一个最小闭环。

import re import json from pathlib import Path def split_by_section(text: str): # 按【】切分为语义块,保留标题作为上下文前缀 sections = re.split(r'(?=【)', text) result = [] for sec in sections: sec = sec.strip() if sec: result.append(sec) return result def main(): raw = Path("requirement_sample.md").read_text(encoding="utf-8") chunks = split_by_section(raw) all_cases = [] for chunk in chunks: print(f"正在处理片段: {chunk[:20]}...") output = extract_and_generate(chunk) # output 中包含 extract_result 和 generated_cases 两部分 extract_result = output.get("extract_result", {}) generated_cases = output.get("generated_cases", []) # 把抽取结果中的 module 补充进每个用例 module = extract_result.get("module", "未命名模块") for case in generated_cases: case["module"] = module all_cases.extend(generated_cases) valid_cases = validate_cases(all_cases) Path("generated_cases.json").write_text( json.dumps(valid_cases, ensure_ascii=False, indent=2), encoding="utf-8" ) print(f"生成完成,有效用例数: {len(valid_cases)}") if __name__ == "__main__": main()

这段代码的关键点有两个。第一是split_by_section使用正则(?=【)做零宽断言切分,既能按标题分段,又不会把“【”这个字符本身吃掉,保留了每个章节的完整语义。第二是validate_cases复用上一节的过滤逻辑,先过滤再落盘,确保生成的文件不包含空步骤用例。

参数方面值得注意两点。第一,chunk的规模不宜过大,单个片段控制在 500 字以内比较好。片段过长时,模型会倾向于只回应开头部分的功能点,尾部细节被忽略。第二,如果你的需求文档包含表格,建议在切片前把表格转换为| 字段 | 类型 | 约束 |的 Markdown 格式,再交给模型,不要直接丢纯文本抽取,否则列与列的对应关系容易丢失。

4.3 生成的用例长什么样:核对“预期”是否真实来自需求

跑完上面的脚本后,拿到的用例大致会是这样一张结构。我摘几行来说明。

用例标题优先级前置条件操作步骤预期结果
创建提醒成功后列表可见P1已登录系统1. 点击记新建提醒按钮;2. 填写提醒时间、内容;3. 保存并回到提醒列表列表新增该条提醒记录,提醒时间显示正确
提醒数量达到上限时新建提醒P1已登录系统,已有 50 条提醒1. 点击“新建提醒”按钮;2. 不进行任何输入弹窗提示“最多可创建 50 条提醒”,不进入编辑页
用户未开通知权限时的到期提醒P2已创建提醒,系统通知权限已关闭1. 等待追踪到提醒时间;2. 观察应用行为应用内消息通知用户,而非系统推送
到期前 10 分钟浮层提示P2用户正在使用应用,提醒期将至 10 分钟1. 停留在任意页面;2. 等待 10 分钟首页展示浮层提示,可关闭

仔细核对会发现,这些用例的预期结果都来自原文的“应推送通知”“应通过应用内消息告知”“应弹窗提示”,没有出现原文未提供的细节。这是提示词中“不要编造需求文档中未提及的字段或规则”这条约束在起作用。如果你发现生成结果里出现“系统向用户发送短信”之类原文没有的描述,回查提示词是否删掉了这条约束,或者温度参数是否调得过高。

5. 避坑与排查:为什么生成结果时好时坏

工具搭起来容易,稳定产出高质量用例才是难点。这一章把我自己踩过的坑和对应的处理方式整理出来,按“现象 → 原因 → 解决”的顺序写,方便你对照排查。

5.1 现象一:功能点漏得厉害,核心路径都覆盖不到

一段时间内我自己遇到过最头疼的现象是:一段 800 字的需求,只生成了 4 条用例,而且都是第一屏的功能,后面的边界条件和异常分支一概没提。

原因分析下来有两个。第一个是输入文本过长,模型对超长上下文的关注度会不自觉地偏向开头和结尾,整体出错概率高于短文本。很多团队在接入这类工具时,会把整份 PRD 一次性丢给模型,导致后半部分需求信息被忽略。第二个是切片太粗,一个 800 字片段里包含 3 个功能点,模型只把第一个功能点写细,另外两个只写了一遍带过。

解决方法是强制切分:单个功能片段控制在 300 到 500 字以内,且切分后做一次“片段完整性检查”——检查每个片段是否有至少一个动宾结构,比如“点击保存按钮”“输入手机号”。如果切片后某个片段是纯表格或纯名词堆叠,说明切片位置不对,这个片段需要和相邻段落合并。切片不正确的输入,模型再强也救不回来。

5.2 现象二:步骤和预期结果不是来自需求,而是模型自编导致的幻觉

有一次给某个系统做验收,模型生成的用例里出现了“系统自动发送邮件给管理员”,翻遍整个需求文档都没有这句。这就是典型的模型幻觉问题。大模型在生成时倾向于补齐“看起来合理”的信息,尤其当提示词允许它自由发挥时,它会补充数据库操作、权限校验、消息通知等常见后台动作。

解决的思路是把输出约束做到位。第一步,在系统提示词里加一句“预期结果必须能在需求文档中找到对应描述,找不到时请标注‘文档未提及’”。第二步,在生成用例后增加一个“可溯源校验”步骤,逐条把预期结果中的关键词与原文做模糊匹配,匹配失败的报告出来,由人工确认是摘录疏漏还是幻觉,再批量修正。自动化校验脚本的核心代码如下:

def traceability_check(case, source_text): expected_keywords = extract_keywords(case["expected"]) missing = [kw for kw in expected_keywords if kw not in source_text] if missing: case["traceability_warning"] = f"以下关键词未在原始需求中找到: {missing}" return case

这里extract_keywords可以简单用 jieba 分词后过滤掉停用词实现。不用做得很复杂,目的是圈出可疑用例,让测试人员优先审核。实际使用中,这层校验能把幻觉用例的漏出率降低一截。

5.3 现象三:生成的用例和现有用例库体系对接不上

很多测试团队已经有成熟的用例模板,比如“预置条件 + 测试步骤 + 预期结果 + 优先级 + 模块归属”。模型默认输出的 JSON 字段名往往和团队模板不一致,直接导入用例管理平台时会出现字段映射失败或数据丢失。我当时对接某测试平台时就遇到过这个问题:平台要求步骤里每个动作前面有序号,模型输出的步骤没有;平台要求优先级只能填四个固定值,模型输出了“中”“高”“低”。

解决方式不是改模型,而是在中间加一层适配器。这层适配器的职责是字段映射、枚举值转换、序号格式化。对应到代码里就是一个转换函数,输入模型输出,输出平台可导入的格式。

PRIORITY_MAP = {"P0": "高", "P1": "高", "P2": "中", "P3": "低"} def adapt_to_platform(case): return { "module": case["module"], "name": case["case_title"], "priority": PRIORITY_MAP.get(case["priority"], "中"), "preconditions": case["preconditions"], "steps": [f"{i + 1}. {step}" for i, step in enumerate(case["steps"])], "expected": case["expected"] }

这层适配器最大的价值是把“模型输出格式”和“平台存储格式”解耦。以后换平台或调整字段,只改适配器,不需要重新生成用例。

5.4 现象四:版本更新之后重复跑,生成结果和上一次完全不同

同一个工具,同一份需求文档,两次生成的用例对不上。这是大模型的天然属性,即使温度设为 0,不同推理框架下也可能因为随机种子不同而出波动。对于测试用例这种需要稳定的场景,波动的影响很大:用例编号无法固化,评审记录难追溯,团队容易失去信任。

我的做法是给用例增加“哈希指纹”。生成完之后,把用例的核心字段拼成一个字符串做 hash,存入用例库。下次生成时先对同一份文档做 hash 比对,如果源文档片段未变化,直接使用上次生成的用例,不做重复生成;只有片段内容变了,才重新生成并标记为“已更新”。

import hashlib def case_fingerprint(case): raw = json.dumps(case, sort_keys=True, ensure_ascii=False) return hashlib.md5(raw.encode("utf-8")).hexdigest()

这样既节省调用成本,也保证了用例资产的稳定性。源文档不变时,用例不重生成,是最省心也最容易说服团队推行该方向的做法。

5.5 现象五:输出看似完整,但缺了“不做什么”

测试用例里除了验证功能实现正确之外,还得覆盖“系统不应当做的事情”。比如某个页面需求写了“支持上传图片”,但没写“不支持上传视频”。模型在生成用例时,会倾向于只在正向场景里验证图片上传成功,而不会主动补写“上传视频应被拦截”之类的检查。除非需求文档里明确写了“不支持”,否则模型不会认为需要生成。

解决方式是建立需求补充清单。当测试人员发现某条规则没有对应用例时,可以把“反向需求”补进录入池,再并入下一次批量生成。也可以直接在需求切片阶段人工标注“重点校验点”,添加到输入文本的末尾,例如“注意:以下是重点校验项,请单独为它们生成用例:不允许上传视频;不允许重复提交订单。”这种方法比修改提示词更直接,也不依赖模型自己发散。

6. 从“能生成用例”到“能信任用例”:覆盖度审计与去重的日常习惯

当工具稳定产出用例后,下一个要解决的问题是:怎么确认生成结果覆盖了需求中所有该测的点。我的习惯是把“覆盖度审计”做成每次生成后的固定环节,而不是偶尔抽检。

做法分两步走。第一步,生成用例后,从需求片段中提取所有“动宾结构”关键词,例如“创建提醒”“编辑提醒时间”“关闭系统通知权限”“触发浮层提示”。第二步,把这些关键词和已生成的用例做一级联动比对,统计哪些关键词出现在用例标题或步骤中,哪些没有。未命中的关键词直接形成“未覆盖清单”。这一步只需要一条简单的 Python 函数就能实现:

def coverage_audit(cases, action_keywords): covered = set() for case in cases: text = case["case_title"] + " " + " ".join(case["steps"]) for kw in action_keywords: if kw in text: covered.add(kw) uncovered = [kw for kw in action_keywords if kw not in covered] return uncovered

这个清单出来之后,把未覆盖的关键词人工归类:是输入文本里压根没有这个规则?还是切片时丢了?还是模型生成的用例确实偏了?根据不同的归类结果,选择修文本或修提示词。这个流程跑习惯后,你会发现质量提升主要来自“动态修正”,方向逐渐收敛到一个稳定的水平。

关于去重,我的规则很简单:功能路径相同的用例保留一条,变化的是测试数据。比如同一个“提醒到期触发推送”的功能,只生成测数据不同的多条用例是冗余的。在工具生成结果里,这种情况很常见——模型会把“提醒内容为文字”“提醒内容为数字”“提醒时间为跨天”各生成一条独立用例。我一般只保留第一条,其余在备注里注明变体条件,不单独占用例库条目。这一步节省的是日后回归测试的时间和用例资产膨胀的存储成本。

最后一个习惯:每个迭代版本,抽 5 条生成用例和对应需求原文,人工做一次“逐字核对”,看预期结果里每一个动作是否都能在原文找到依据。5 条不多,但做三次迭代之后,你会对自己的提示词、切片方式、模型选型建立起真正的信心。我现在每周都会做一次这样的抽检,每次都能发现一两个小问题,但这些都是确定性问题的修正过程,而不是模型完全不可控的玄学问题。做这个方向,最终目标是让团队把时间从“写用例”转移到“做覆盖度决策”上来,那才是这套工具真正值回投入的地方。希望这篇笔记能帮你少走几步弯路,也希望你跑通之后,把你踩到的坑再反馈回团队,这个方向才能越磨越稳。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询