AI Agent 测试工作流复盘

两周前,我在禅道上提了一个 Bug。开发者修完了,MR 合并了,按流程我该写回归用例了。
按惯例应该是:打开飞书文档 → 重新理解需求 → 打开 GitLab 看 diff → 翻历史 Bug → 打开禅道写用例 → 填写标题、步骤、预期 → 回填关联需求 ID。一套下来,这个回归用例至少半小时。
这次不一样。我打开了 Hermes 终端,敲了一行:
1 | 帮我生成测试用例,需求在飞书 XXX,MR 在 XXX |
然后 Hermes Agent 开始了它的工作:读飞书文档、拉 GitLab diff、查历史缺陷、分析代码风险信号、写测试点、生成详细用例、写入禅道、回填 ID。
3 分钟不到,30 条相关的测试用例完美地躺在禅道里,P1/P2/P3 分布合理,覆盖正向/异常/边界/UI/幂等五个维度。其中有 11 条用例的测试角度来自代码 diff 分析——需求文档里根本没写这些边界。
这不是工具安利。这是一次关于「如何把工程思维应用到 AI 工作流设计」的系统性实验记录。
1. 痛点:一个 QA 的工具切换日常
我日常使用的工具链并不复杂:
- 禅道 — 测试用例、Bug、任务的唯一数据源
- GitLab — 前后端代码仓库,MR diff 里藏着真正的变更细节
- 飞书 — 需求文档和 Wiki 知识库
- zread — 代码文档辅助工具
每个需求测试的完整链路是:飞书读需求 → 本地读 GitLab diff 理解变更 → 禅道查历史同类 Bug → 设计测试点 → 写用例 → 手动录入禅道。
这个流程最大的问题是:所有上下文切换和信息筛选都依赖人的经验和记忆力。需求文档写了 80% 的逻辑,剩下 20% 的边界和异常藏在 MR diff 里。读漏了就是漏测。
另一个问题是质量一致性。同一个团队里,不同人写的测试用例风格差异很大:有的写「验证功能正常」这种无法判断的预期,有的把步骤写成技术接口描述而非业务操作,有的忘了覆盖边界场景。不是态度问题——是缺少一个统一的「用例质量护栏」。
2. 方案设计:结构、技能与工作流
2.1. 设计思路:为什么是三阶段
一开始很容易想到的架构是「一条 prompt 搞定」——给 AI 所有上下文,让它直接输出禅道格式的用例。试过,效果不行。问题不在于 AI 理解不了需求,而在于没有纠错环节的单次生成,质量方差太大。
我换了一个思路:把测试过程拆成知识理解 → 方案设计 → 执行录入三段,每段产出独立、可审查、可修正。
1 | 阶段一:知识归档(理解) |
这个拆分有三个好处。第一,知识归档和用例编写分离,避免 AI 在信息不全的情况下强行输出。第二,每个阶段结束时都有一个确认点,人可以介入纠偏——这在 AI 工作流中极其重要,因为 AI 在阶段一犯的错,到阶段三才发现,回退成本很高。第三,阶段的边界也是 skill 的边界——后续测试点改模板、换工具链、改知识库结构,影响范围是可控的。
2.2. 项目结构
测试用例的产出不是一个孤立动作,它依赖知识库、模板、历史记录。我把这些组织成一个项目目录:
1 | allInOne/ |
这里有一个关键设计:knowledge 目录作为 AI-first 知识库。每个术语页都有统一的 frontmatter 元数据,用 Obsidian 的 Multi Properties 插件维护,AI 检索时按 _index.md 导航快速定位。业务术语(如 Stand Alone TP、Baseline、Bottleneck)不是散落在需求文档里,而是集中在知识库中,随时可查。
2.3. Skill 体系:把工程约束编码为 AI 可执行的规程
Hermes Agent 的核心机制是 skill——相当于 AI 的「可执行规程」。我构建了三个核心 skill:
① test-case-writing(测试用例编写护栏)
这个 skill 不做「怎么写用例」的教程——教程放在工作流和模板里。它只做两件事:执行纪律和红线提醒。
执行纪律包括:写用例前必须先读工作流文件、必须按三阶段执行、每阶段完成后等确认。红线提醒包括:不测防御性 guard clause、不测编译时保证、不测业务不可达状态、不写纯技术 API 参数校验类用例。
它还有一个阶段二自检清单——每条用例在提交确认前,检查步骤数是否等于预期数、是否违反有效性红线、前置条件是否完整、步骤是否可执行、预期是否可观察。
② testcase-pipeline(8 步工作流)
对应三阶段八步的完整执行流程,定义了每一步的输入输出和确认点。包含风险评分规则(按改动文件数、代码行数、是否含异步任务、是否有数据库变更评估 🟢/🟡/🔴),以及实战中踩过的坑(跳过阶段一直接写用例导致返工、格式参考应该看模板而非凭记忆等)。
③ zentao-cli(禅道 CLI 操作参考)
封装了 zentao-cli 的完整命令参考,包括认证方式、常用操作、明确的陷阱——比如 workspace set 不会自动注入 –product、testcase update 必须传全量字段否则会被清空、创建测试用例时 steps/expects 数组长度必须一致。这些陷阱都是真实踩过之后补上的。
这三个 skill 的协作方式是:用户说「生成测试用例」→ Hermes 加载 testcase-pipeline → 该 skill 强制要求先加载 test-case-writing → test-case-writing 强制要求先读工作流文件 → 开始执行。每层都在做「确保不被跳过」的事。
2.4. 质量护栏:从 prompt 到工程约束
这个体系中最反直觉的设计是:工作流和模板是文件,不是 skill 的一部分。
skill 负责「提醒你必须读文件」,但不复制文件内容。工作流只有 工作流/testcase-pipeline.md 一份,模板只有 模板集/ 下的三个文件。如果某天需要改流程,只改一个文件;需要改格式,只改对应的模板文件。skill 不需要更新。
这会牺牲一些便捷性——每次执行前都要读一遍文件。但保证了当流程或模板变化时,不会出现「改了 A 忘了改 B」的多源冲突。对于长期维护的系统来说,单一真相来源比便捷性重要得多。
3. 第一次跑通:Auto Baseline 需求
第一个真实跑通的需求是「Auto Baseline 自动更新任务」。一个涉及保存系数、任务管理、分配任务、执行日志、恢复回滚的前后端协同功能。前端 MR 5726 行 diff,后端涉及三个微服务。
30 条用例最终写入禅道(#1782~#1810),覆盖六个模块:
- 保存系数(3 条)
- 新建/管理自动更新任务(5 条)
- 分配自动更新任务(4 条)
- 自动更新执行/日志/恢复(6 条)
- 前端 UI/交互(7 条)
- 后端 API/边界(4 条)
有趣的数据是:30 条用例中,11 条的来源标注为「代码特性」而非「需求文档」。具体来说:
| 代码信号 | 对应测试点 | 需求文档是否覆盖 |
|---|---|---|
closeOnOutsideClick=false |
测试 ESC 和外部点击关闭弹窗 | 否 |
hasNegativeValue() |
测试负数输入阻断 | 否 |
executionLogRestoreThrottleMs=500 |
测试双击节流 | 否 |
@Valid 参数校验 |
测试服务端必填参数校验 | 否 |
这些边界条件不是 AI 凭空生成的——它读了 MR diff,看到代码里写了这些分支,然后判断「这是正常用户能触发的操作路径吗?」如果是,就转化为测试用例。
第一版我生成了 15 条用例——步骤模糊、预期不可判断、缺少边界。第二版按质量标准重写后变成 30 条。AI 第一次产出的质量并不能直接交付,但迭代成本远低于从零开始写。
4. 第二次跑通:一个真实 Bug 的回归验证
第二个测试是 Bug #1457 的回归。问题很具体:Event Timeline 页面中,用户从其他页面选了班次后切过来,按钮一直显示班次编号(如「1.7」),选完自定义时间后按钮文案仍然停留在「1.7」,造成认知冲突。
MR !423 改了四个东西:
- 按钮标签从智能缩写改为始终显示完整日期格式
- 弹窗改为响应式缩放(按视口等比缩小,最小 0.35 倍)
- DateBox 下拉改为挂到 document.body,避免小弹窗内 overflow 裁切
- 自定义 closeOnOutsideClick,防止点日历被当成「外侧点击」关掉弹窗
最终产出 14 条用例,覆盖按钮格式(同天/跨月/跨年/取消回退)、弹窗响应式(正常/小屏/极屏/resize)、点击行为(日历/遮罩/内容)、布局兼容性四个模块,全部写入禅道 #1828~#1841。
这轮测试验证了一个事情:流程对 Bug 回归场景同样有效。不需要飞书文档,直接从禅道取 Bug → 读 MR diff → 查历史 → 归档 → 写用例 → 上传,全链路可跑。
5. 意外收获
流程设计没有问题,但工具调用层面的细节让我印象更深。
5.1. 错误一:前置条件没传上去
14 条用例全部写入成功,标题、步骤、预期都正确。但禅道的「前置条件」字段全是空的。
排查过程:从禅道查 testcase <id> --pick=precondition,返回空字符串。回到代码看——precondition 字符串里含 \n(换行符),在 f-string 拼 shell 命令时变成了实际换行,zentao-cli 的参数解析器在换行处截断了参数。
教训:传给 CLI 的参数值不能带换行符。用中文分号「;」代替 \n。这个细节后来写进了 test-case-writing 的 lessons-learned 里,下次执行前自动加载。
5.2. 错误二:回填 ID 时 replace_all 翻车
14 条用例写入成功后需要回填禅道 ID。用了 replace_all=true——结果所有 14 条的「禅道ID:未上传」都被替换成了同一个 ID「#1828」。
教训:批量替换用行号逐条赋值,不用全局替换。
5.3. 错误三:步骤数和预期数不一致
阶段二自检时发现,有几条用例的测试步骤数量与预期结果数量不一致。多一步或少一步,上传时 steps 和 expects 数组长度不匹配。
教训:预期结果用有序列表(1. 2. 3.)和步骤编号对齐,是结构层面的预防手段。
这三个错误都不是流程设计问题,都发生在「AI 调用外部工具的边界」上——如何正确构造 shell 命令、如何做批量文件操作、如何保证结构化数据的长度一致性。AI 工具链的成熟度,很大程度上取决于这些边界细节的处理。
6. 现状与下一步
目前的体系完整架构如下:
1 | 用户输入(禅道任务 / MR 链接 / 飞书文档) |
它的能力边界是:输入一个禅道任务或 MR 链接,能独立跑完从知识归档到写入禅道的完整链路。两次真实验证都跑通了,产出直接可用。
最后,附上testcase-pipeline.md文档:
1 | # 测试用例生成工作流 |
回头看,这个实验的核心收获不是「AI 能写测试用例」——现在大家都能让 AI 写文本。核心收获是:当 AI 被嵌入一个有明确阶段划分、有质量护栏、有回退机制的工作流时,它的产出才是可交付的。
没有这些约束,AI 产出 15 条质量不达标的用例只需要 2 分钟。有了这些约束,AI 产出 30 条可直接上禅道的用例也只要 3 分钟——多出来的 1 分钟花在读取知识库、分析代码 diff、自检和修正上。这是用工程思维让 AI 从「快速产出废物」变成「慢速产出可用品」的关键一步。
AI 不会替代测试人员。但会有一批测试人员,用 AI 重新定义自己的工作流。