通用服务测试 AI 化实践:从 AI 友好框架到两阶段流水线
一、写在前面
高变更密度业务的服务端测试,长期被三件事卡着脖子。
变更密集。一个迭代里,多个业务模块的接口往往同时在改,单纯靠 Code Review + 经验回归,漏测风险高。
Case 编写成本高。一条标准的 TestNG Case,从读懂 PRD 到补齐数据准备、清理、断言。
知识孤岛。需求在需求管理平台,技术方案在协作文档/知识库,代码在代码托管平台,历史用例在测试仓库——人需要在四五个系统间反复切换才能写出一条像样的 Case。
我们沿着一条核心思路推进:
让 AI 把跨系统上下文串起来,AI 总结梳理"测什么",再让 AI 把它翻译成可执行的代码"怎么测"。
最终落成了一条两阶段 AI 流水线:
| 阶段 | 角色 | 输入 | 输出 |
|---|---|---|---|
| 一 | 需求分析 Agent | 需求文档 + 技术方案 + 代码变更 | 变更分析报告 + 测试点清单(测什么) |
| 二 | 代码生成 Agent | 测试点 + 测试框架 + 历史 Case | 测试方案 + Case 代码(怎么测) |
目前框架已覆盖多个核心业务模块,累计数百个自动化 Case。单接口 Case 编写从平均20 分钟降到 8 分钟以内,复杂需求(涉及多模块联动)的编写工作量从5~10 人天降到 3 人天以内。
但在讲这两阶段之前,我想先讲一件容易被忽视的事:这套链路能跑通的真正前提,不是模型多强,而是测试框架本身就是"AI 友好"的设计。
二、大前提:测试框架的 AI 友好设计
我们沉淀出一套 AI 友好的测试框架,核心设计原则如下。
2.1 沉淀 AGENT.md:给 AI 一份"说明书"
在仓库根目录放一份AGENT.md(面向 AI 的项目说明),把构建命令、模块结构、目录约定、技术栈、命名规范、单 Case 启动方式全部写清楚。AI 编码 Agent 启动时自动读取,从此不再需要在 prompt 里反复解释项目背景。
2.2 统一入口:消除歧义
框架只保留test-case-start模块下的 Spring BootTestNgRunner作为唯一入口。所有 Case 统一走mvn test -pl test-case-service -Dtest=...或平台 Web UI 触发。模型只需记住一种调用方式。
2.3 严格四层 TestBase 体系
TestDataBase(抽象基类) │ 提供:用户标识/设备标识/请求标识 生成、登录注册、Cookie 管理 │ ├── {Module}TestBase(ModuleATestBase / ModuleBTestBase / ModuleCTestBase ...) │ 提供:各域服务 IP 初始化(@Parameters + @Optional) │ │ ├── {Domain}TestBase(DomainXTestBase / DomainYTestBase ...) │ │ 提供:领域参数构造(buildSkuFull / buildSkuOrder / InfoOrderMQBody ...) │ │ │ │ └── 具体 *Test 类(CalcOrderTest / OrderUsableDiscountsTest ...)每层职责明确,AI 写新 Case 时只需找到对应领域的TestBase继承即可。
2.4 三套辅助类:TestData / DataHelper / MockData
每个业务模块对应三个辅助类,职责严格分离:
| 类型 | 示例 | 职责 | 规则 |
|---|---|---|---|
{Module}TestData | ModuleATestData、DomainXTestData | 只定义常量:业务 ID、活动 ID、POI ID、枚举 | 类内禁止包含任何方法 |
{Module}DataHelper | ModuleADataHelper、BudgetDataHelper | 只提供静态方法:Redis(库存/限频/资格)、DB(数据查改删/流水日志) | 禁止定义数据常量 |
{Module}MockData | ModuleAMockData | 只生成 Mock 数据:算法 Mock、外部服务 Mock 响应 | 与 TestData/DataHelper 解耦 |
三套类的分工让 AI 有了确定的选择路径:
查数据 → TestData,操作 Redis/DB → DataHelper,Mock 外部依赖 → MockData
这几乎消灭了"臆造方法名"的问题。
2.5 API Client 标准化
com.example.test.apis.*下每个接口一个 Client 类,全部继承ApiV2Base,签名、加密、Header 注入由基类统一处理。AI 只需三步固定模式:
XxxApi.getInstance().ip=getTestHost();// 1. 指定服务地址JSONObjectresp=XxxApi.getInstance().call(params);// 2. 发送请求Assert.assertEquals(resp.getString("code"),"SUCCESS");// 3. 校验返回2.6 公共常量收敛
| 文件 | 作用 |
|---|---|
ServerIpDaily | 所有 daily 环境服务 IP 的单一事实源 |
conf/Config.xml | 平台默认参数(channel、device info、用户标识 等) |
common/constants/BizTypeConstants | 业务类型常量 |
common/constants/CategoryConstants | 类目 ID 常量 |
common/constants/StatusConstants | 状态枚举 |
common/constants/ParamsConstants | 公共默认参数(adcodeDefault / appVersionDefault) |
AI 引用时直接import static,不会再硬编码或臆造。
2.7 强制六段注释模板
每条 Case 头部强制写出六段注释:
测试目标 → 测试场景 → 前置条件 → 测试步骤 → 预期结果 → 关键验证点
把人类的设计逻辑显式落进代码。它既是规范,也是 AI 下一次召回时的高质量训练语料。
关键结论:框架做到 AI 友好后,同一份 prompt 同一个模型,Case 编译通过率达到91%。框架设计的 ROI 远高于 prompt 调优。
三、阶段一:AI 分析"测什么"
3.1 输入来源
| 输入 | 来源 | 内容 |
|---|---|---|
| 需求文档 | 需求管理平台工作项 | PRD 正文 + 验收标准 + 关联的协作文档/知识库 |
| 技术方案 | 协作文档 / 知识库 | 接口契约、时序图、新增/修改 DDL |
| 代码变更 | 代码平台 MR | changed files + diff(仅 src/main 业务层)+ 提交说明 |
需求分析 Agent 的核心优势是通过内部 MCP 工具(如list_changed_files、searchCodeWiki、query_workitem_detail、get_changed_file_diff等),直接读到第一手数据,不需要手动复制粘贴。
3.2 调用流程
3.3 Prompt 模板(节选)
# 角色定义 你是一位专业的 Java/Go 代码变更分析与服务端测试专家。 核心职责: - 代码变更深度分析(语法结构、实现逻辑、调用关系) - 服务端接口测试方案设计与用例编写 - 多仓库复杂变更场景的影响评估 # 一、任务执行流程(严格按序执行,不可跳步) ## Phase 1:获取变更单详情 **输入**: crId **动作**: 调用接口查询变更单详细信息 **输出解析规则**: - `repo`: 从 `branchUrl` 中解析仓库名称 - `sourceBranch`: 从 `branchUrl` 中解析分支名 - `detailUrl`: 直接提取 ## Phase 2:获取变更文件列表 **动作**: 对比 `sourceBranch` 与 `master` 分支 **输出**: 变更文件列表(含路径) ## Phase 3:分支处理(条件分支) IF 文件列表为空: 1. 在 repo 中搜索 sourceBranch 的已合并 MR → 获取 mergeRequestId 2. 通过 mergeRequestId 查询 MR 变更文件列表 3. 逐文件获取 diff(对比 master) ELSE: 直接逐文件获取 diff(对比 master) **校验点**: 每次工具调用后验证返回数据非空且格式正确,异常时立即报告并停止。 # 二、变更分析要求(五阶段递进分析) ## 阶段 1:项目上下文建立 **目标**: 建立项目认知基线 执行动作: 1. 查阅项目 CodeWiki 文档 2. 查阅需求文档(如有) 3. 查询项目知识库 4. 理解项目架构、代码组织结构、命名约定 5. 识别业务领域关键概念和术语 **注意**: 领域术语可能有特殊含义(如 checkIn/checkOut 在不同业务中的含义差异),必须结合上下文确认。 ## 阶段 2:逐文件变更分析 **目标**: 对每个 diff 文件进行四维度分析 | 维度 | 分析内容 | |------|---------| | 代码变动点 | 精确标注新增/删除/修改的代码行及行号 | | 字段语义 | 推断新字段的业务含义,结合注释和文档确认 | | 方法逻辑 | 描述涉及方法的功能、输入输出、实现逻辑 | | 调用关系 | 构建方法间、模块间的调用图谱 | **约束**: 忽略 `_test.go` 文件;关键结论必须引用具体 `文件路径:行号`。 ## 阶段 3:业务流程与数据生命周期分析 **目标**: 理解数据的完整生命周期 分析要点: - 数据从创建到消费的完整链路 - 数据流转中的状态变化 - 兜底逻辑和默认值处理 - 数据校验与转换节点 ## 阶段 4:影响范围评估 **目标**: 多维度评估变更影响 影响分析树: ├── 影响的接口(哪些 API 被修改,URL + 方法) ├── 影响的业务类型(哪些业务场景受影响) ├── 影响的数据状态(哪些状态流转被改变) └── 影响的具体字段(哪些字段被新增/修改/删除) ## 阶段 5:综合总结 **目标**: 结构化归总 - 按仓库分类变更内容 - 分析仓库间依赖和交互关系 - 整理 `仓库 → detailUrl` 映射 # 三、输出物 1:变更分析评估报告 **格式**: Markdown **结构(严格按序)**: 1. 变更基本信息 2. 变更概述(基于阶段 1 的项目认知) 3. 变更文件详情分析(基于阶段 2、3) 4. 调用链路分析 5. 深度技术分析 6. 风险评估分级(P0/P1/P2) 7. 边界条件挖掘 8. 生产环境适配考量 9. 测试要点(按优先级排序) 10. 风险总结和建议 11. 变更链接汇总 # 四、测试用例设计(三核心原则 + 七阶段流程) ## 三核心原则 | 原则 | 说明 | |------|------| | 前置条件分析 | 明确被测接口的数据准备链路(哪些接口需要先调用、什么顺序) | | 完整业务链路 | 每个用例必须包含:`前置接口/条件 → 被测接口 → 验证结果` | | 三层覆盖策略 | 主流程 + 边界条件 + 异常场景,缺一不可 | ## 七阶段执行流程 ### 阶段 1:知识准备 - 基于变更分析阶段 1 的项目认知,理解业务场景和规则 - 基于变更分析阶段 4 的接口维度,确定被测接口集合 ### 阶段 2:前置条件分析(每个接口必答) 对每个被测接口,必须明确回答以下 4 个问题: Q1: 需要先调用哪些接口来准备测试数据? → 列出所有必需前置接口及其作用 Q2: 数据依赖关系是什么? → 绘制依赖链:接口A → 接口B → 接口C → 区分强依赖(必须)和弱依赖(可选) Q3: 哪些前置条件是必需的?哪些是可选的? → 必需:缺少则无法执行测试 → 可选:用于构造特殊场景 Q4: 前置接口失败时,被测接口应如何处理? → 验证异常处理和降级策略 ### 阶段 3:完整业务链路设计 对每个接口,按以下模板设计: ┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐ │ 前置接口(数据准备) │ → │ 被测接口 │ → │ 验证结果 │ └──────────────────┘ └──────────────┘ └──────────────────┘ **示例**: 测试优惠选择功能: 前置:用户登录 → 优惠列表查询 → 订单详情获取 被测:选择接口 验证:返回结构检查 + 优惠金额计算 + 活动优先级判定 测试价格计算功能: 前置:活动配置 → 用户身份确认 被测:报价接口 验证:是否命中优惠 + 优惠金额 + 总金额正确性 ### 阶段 4:三层覆盖策略 | 层次 | 说明 | 设计要点 | 示例 | |------|------|---------|------| | 主流程 | 正常业务场景 | 最常见的用户使用路径 | 正常登录→正常选择→正常下单 | | 边界条件 | 临界值场景 | 最大值、最小值、阈值临界点 | 金额=0、数量=1、版本=阈值 | | 异常场景 | 错误处理场景 | 空值、超时、非法参数 | 负数金额、空token、超时响应 | ### 阶段 5:服务交互验证 验证清单: ├── 下游服务返回的数据结构是否正确 ├── 上游服务最终命中的规则是否符合预期 ├── 返回结构完整性(如 data.alternativeDiscounts) ├── 优先级逻辑(如 特价活动 > 其他优惠) └── 状态流转(可用/不可用列表) ### 阶段 6:测试数据规范 **强制要求**: 使用具体、可量化的测试数据。 ✅ 正确: - 普通补贴 10 元 + 叠加券 4 元 vs 返现补贴 10 元 - 特价补贴 8 元 vs 追价补贴 3 元 + 叠加券 4 元 - clientVersion=5.2.0(阈值 5.0.0)时的行为差异 ❌ 禁止: - "测试优惠组合的 PK 逻辑"(模糊) - "测试不同版本的行为"(无具体值) - "测试金额计算逻辑"(无具体数据) ### 阶段 7:用例知识融合 生成用例时必须结合: - 项目知识库中的业务规则 - 代码中的注释说明 - 文档中的约束条件 # 五、输出物 2:测试用例文档 **格式**: Markdown(.md) **文档结构**: markdown # 测试用例文档 ## 接口 A: [接口名称] - **接口 URL**: [具体URL] - **接口方法**: [GET/POST/...] ### 正常场景 #### 前置条件 - 配置:[具体配置项,如满减/折扣/补贴/活动] - 登录:[用户身份要求] - 数据准备:[需要调用的前置接口及参数] #### 测试用例 | 用例编号 | 优先级 | 场景描述 | 测试数据(请求参数) | 预期结果 | |---------|--------|---------|-------------------|---------| | TC-A-01 | P0 | ... | {...} | ... | ### 边界场景 [同上结构] ### 异常场景 [同上结构] ## 接口 B: [接口名称] [同上结构] ## 风险与注意事项 - [风险点1] - [风险点2] # 六、全局约束(强制执行) | 约束项 | 规则 | |--------|------| | 文件过滤 | 忽略所有 `_test.go` 文件 | | 代码引用 | 关键结论必须引用 `文件路径:行号` | | 业务术语 | 使用业务领域专业术语,不可自造术语 | | 完整链路 | 每个测试用例包含完整的前置条件和验证步骤 | | 优先级定义 | P0=核心功能必须通过,P1=重要功能,P2=边界/异常 | | 数据具体性 | 测试数据必须具体可量化,禁止模糊描述 | | 执行顺序 | 严格按步骤顺序执行,不可跳步 | | 异常处理 | 工具调用失败时立即报告并停止 | | 知识融合 | 用例生成必须结合知识库、代码注释、文档 | # 七、最终自检清单(生成输出前必须逐项确认) - [ ] 是否分析了业务语义(字段在业务中的真实含义) - [ ] 是否梳理了完整的数据生命周期(创建/流转/使用/销毁) - [ ] 是否明确了每个接口的前置接口(数据准备链路) - [ ] 是否设计了完整业务链路(前置 → 被测 → 验证) - [ ] 是否覆盖了三层(主流程 + 边界条件 + 异常场景) - [ ] 是否引用了代码依据(文件路径:行号) - [ ] 是否使用了正确的业务术语 - [ ] 是否忽略了单元测试文件 - [ ] 测试数据是否具体可量化 - [ ] 输出格式是否符合指定结构3.4 产出示例
以「某业务修改动态规则描述」为例,Agent 一次性产出两份文档:先给一份"高位的"变更分析评估报告,再给一份"落地的"测试用例文档。两份文档都是 Markdown,可直接贴进工作项或协作文档评审。
文档一:变更分析评估报告
(示意图:变更分析评估报告样例)
文档二:测试用例文档
(示意图:测试用例文档样例)
到这一步,测试同学要做的只有一件事:审校测试点。体感是:80% 直接可用,15% 微调,5% 误报——而误报里有一半反向暴露了 PRD 的不严谨,反推需求收敛。
四、阶段二:告诉 AI"怎么测"
4.1 两步走:先方案后代码
最早我们让需求分析 Agent 一把梭直接出 Case 代码,效果不好。框架细节是项目独有的,模型对*TestBase层级、TestData/DataHelper/MockData分工没有先验;一次性生成几百行代码,人 Review 时被语法细节抓走,忽略测试逻辑。
阶段二拆成两个显式动作:
Step 1— 生成自然语言测试方案(前置操作 / 步骤 / 断言),方便人审
Step 2— 方案确认后生成Case 代码,走标准继承 + 标准 Client + 六段注释
4.2 上下文组装
代码生成 Agent 启动时自动读AGENT.md获取项目骨架,额外组装:变更分析评估报告 + 测试点清单 + 自动化框架仓库召回的历史 Case。
System Prompt 核心约束(对应框架设计原则):
你是测试框架下的测试方案设计师与代码作者。铁律如下: 1. 继承:基于 {Domain}TestBase(如 DomainXTestBase),不得创建新基类 2. 数据:常量从 {Module}TestData 取,禁止硬编码 3. 操作:Redis/DB 调用 {Module}DataHelper 静态方法 4. Mock:从 {Module}MockData 取 5. 接口:复用 apis.* Client(extends ApiV2Base),禁止裸 OkHttp 6. IP:从 getTestHost() / ServerIpDaily 取 7. 注释:每条 Case 六段注释(目标/场景/前置/步骤/预期/验证点) 8. 不确定:TODO(原因) 占位,绝不臆造 9. 顺序:先产出自然语言方案,确认后再写代码4.3 中间产物:自然语言测试方案
(示意图:自然语言测试方案样例)
人 Review 只需读 30 行文字,关注业务覆盖是否完整;机器消费时每一句直接映射 Java 语句。
写完后代码生成 Agent 自动跑mvn test,编译/断言失败即回退到方案层重审。
五、踩过的坑与最佳实践
5.1 框架设计决定 AI 上限
框架的"AI 可理解程度"直接决定 AI 生成代码的质量上限。入口统一、继承清晰、常量收敛、文档齐备——这四点做到位,编译通过率可以稳定在 90% 以上。反之,prompt 写得再好也是徒劳。
5.2 三套辅助类是关键脚手架
最初把数据常量、Redis 方法、Mock 构造揉在一个类里,AI 无从判断用哪个方法。职责分离后选择路径确定:查数据 → TestData,操作数据 → DataHelper,Mock → MockData。几乎消灭了臆造方法名。
5.3 上下文不是越多越好
PRD 5 万字 + diff 3000 行一股脑塞进去,模型抓错重点。引入"变更切片器"(按包路径白名单过滤 diff,按章节锚点切 PRD),控制在 8k token 以内,召回率反而更高。追求"相关上下文",而非"长上下文"。
5.4 测试方案用自然语言
试过 YAML/JSON 中间层:人被缩进干扰,模型被压抑漏覆盖。自然语言格式与六段注释天然对齐,模型生成代码时几乎是"翻译"工作量。
5.5 让模型学会说"不知道"
强制TODO(原因)占位后,编译返工率从 28% → 6%。模型显式标注不确定,远好过编一个看似合理但不存在的方法名。
5.6 不要省略 Code Review
AI 生成的 Case 仍走标准 MR 流程。CI 门禁:任何 AI 生成的 Case 必须有人类 Reviewer +1,不允许"AI 写、AI Review、AI 合并"闭环。