slug: test-plan-writer name: test-plan-writer version: "1.0.12" changelog: - "1.0.12: 扩充知识库查询范围,增加服务相关查询维度(测试策略、数据筛选、功能开关、预期差异、业务场景)" - "1.0.11: 修复changelog格式、恢复description字段" - "1.0.9: 更新test-plan-template.md测试执行方式说明,区分Jenkins MCP与本地执行,移除压测流水线" - "1.0.8: 同步qa-integration-pipeline取消压测,required_phases更新为[1,2,3,4,5,6],Phase 6改为汇总报告" - "1.0.7: 明确sourceFiles为source_files.txt索引文件路径,更新检查清单A操作步骤" - "1.0.6: 对照组白名单支持独立driver_id,完善experiment_config说明" - "1.0.4: 修正changelog、补充experiment_config与分组预期差异" - "1.0.3: 增加 experiment_config 实验/灰度配置字段、分组预期差异格式、required_phases 映射更新(Case 生成必须执行,压测暂不执行)" - "1.0.2: 优化字段存在性检查、Thrift/Proto区分搜索、字段来源约束、待确认标注规范、自检清单" - "1.0.0: 初始版本,支持标准 test_plan.md 生成" displayName: "Test Plan Writer" description: "基于 PRD 和代码变更分析,生成标准化测试方案(test_plan.md)。职责包括:设计测试场景、定义数据筛选条件、评估风险等级、决定所需流水线阶段。"
本 Skill 是方案设计层,专注测试方案生成,不执行具体测试。
职责:
1. 分析 PRD 需求与代码变更范围
2. 设计可观测协议字段级别的测试场景
3. 定义数据筛选条件(供下游 search-data 使用)
4. 评估风险等级,决定 required_phases
5. 生成标准化 test_plan.md
| 参数 | 必填 | 说明 |
|---|---|---|
feature |
✅ | 功能名,用于目录命名和方案标识 |
designDoc |
✅ | map-spec 设计文档路径(.claude/workflow/<slug>/design.md) |
sourceFiles |
map-spec 传入的 source_files.txt 文件路径(.claude/workflow/<slug>/source_files.txt)。内容为纯文本,每行一个变更文件路径。仅用于辅助了解改动范围,不用于需求分析 |
在生成测试方案前,必须完成以下检查。检查项分为两类:
| 类型 | 含义 | 未通过处理 |
|---|---|---|
| 🔴 阻塞项 | 必须修复,禁止继续 | 返回对应步骤修正 |
| 🟡 警告项 | 允许标注后通过,但需在输出中显性标注 | 记录到自检清单 |
| 检查项 | 操作 | 通过标准 | 未通过处理 |
|---|---|---|---|
| 读取 sourceFiles | read_file <sourceFiles> 获取索引文件,解析每行得到代码文件路径列表 |
索引文件已读取且至少包含一个有效路径 | 标注 [警告: sourceFiles 为空],继续 |
| 读取代码文件 | 对解析出的每个路径执行 read_file <路径> |
关键文件内容已读取 | 标注 [待确认: 需开发确认],继续 |
| 提取日志关键字 | grep_code regex="LOG.*<函数名>" path="<代码文件路径>"(对列表中各文件分别搜索) |
找到日志语句 | 标注 [待确认: 需开发确认],继续 |
| 提取 Apollo Key | grep_code regex="GetConfig|apollo" path="<代码文件路径>"(对列表中各文件分别搜索) |
找到配置读取 | 标注 [待确认: 需开发确认],继续 |
| 确认改动分支 | grep_code regex="if.*event_type.*5" path="<代码文件路径>"(对列表中各文件分别搜索) |
找到条件分支 | 标注 [警告: 未找到相关分支],继续 |
为什么不阻塞? sourceFiles 可能为空(map-spec 未传入),或代码变更未提交到 git。允许降级为"待确认"标注,但必须在输出中显性记录。
| 检查项 | 操作 | 通过标准 | 未通过处理 |
|---|---|---|---|
| Thrift 搜索 | grep_code regex="<字段名>" path="src/navi-guide/src/proto/thrift" |
找到字段定义 | 阻塞:禁止虚构字段 |
| Proto 搜索 | grep_code regex="<字段名>" path="src/navi-guide/src/proto/protobuf" |
找到字段定义 | 阻塞:禁止虚构字段 |
| 建立字段来源表 | 记录字段所属消息和行号 | 每个字段都有来源 | 阻塞:必须补充来源 |
为什么阻塞? 虚构协议字段会导致测试方案完全不可执行,下游 search-data 和 diff 都会失败。这是底线。
| 检查项 | 检查内容 | 通过标准 | 未通过处理 |
|---|---|---|---|
| 数值量化 | 包含具体数值或范围 | 如"数量 >= 1"而非"有效" | 阻塞:返回修正 |
| 字段结构 | 包含具体的字段名 | 如"forkPoint 非空" |
阻塞:返回修正 |
| 识别方法 | 内部状态的识别规则 | 如"通过 multiRoadsForkPoints 非空识别" |
阻塞:返回修正 |
禁止使用的模糊词:正常、有效、正确、成功、合理、优化、提升、改善、包含有效信息、解析成功
例外处理:若确实无法量化(如用户体验类优化),在 说明 中写明量化困难原因和替代验证方式 → 降为 🟡 警告项,允许通过。
designDoc提取:功能描述、生效场景、改动点、约束条件、涉及的协议字段
提取测试设计要素
trigger_condition:生效场景、约束条件expected_behavior:功能描述中的预期输出affected_fields:涉及的协议字段从 designDoc(map-spec 设计文档)中提取:
- 功能描述与生效场景
- 涉及的协议字段(如 ttsContent、voiceType、eventType)
- 触发条件(如 scene_type == F_FORK && distance <= 150m)
- 功能开关控制:
- 开关类型(灰度 / AB实验 / Apollo配置 / 无)
- Apollo Key、默认值
- 若是灰度:白名单内容
- 若是 AB实验:实验组/对照组白名单、实验ID
- 若是 Apollo配置:本地配置是否生效
- 约束条件(如"不影响普通十字路口")
- 改动点(文件、函数、插入位置)
可查询信息源(AI 在生成方案时可主动查询):
| 信息源 | 查询方式 | 用途 |
|---|---|---|
| 知识库(LLM-Wiki) | 调用 http://10.190.56.84:8080/search 语义检索与 feature / sourceFiles / 字段名 / 服务相关关键词 |
了解同类功能的历史测试方案、已知问题、协议字段含义、边界条件、服务部署配置、测试策略决策、数据筛选经验、diff 分析规则、回归测试经验 |
| 历史用例 | 检索 src/navi_guide_tools/case/ 下同类功能的测试用例 |
复用或参考历史场景的 trigger_condition、数据筛选条件 |
| 协议定义 | 读取 src/navi-guide/src/proto/ 下的 .thrift / .proto 文件 |
提取协议中实际存在的字段名、字段类型、嵌套结构,作为黑盒测试的唯一输入输出依据 |
| Apollo 配置 | 检索相关配置项或白名单定义 | 确认开关类型、白名单内容、AB 实验分组规则 |
| 代码变更 | 读取 sourceFiles 中的具体代码 diff |
确认改动点、新增字段、条件分支逻辑 |
designDoc是主要信息来源,已包含足够的需求描述和变更范围。当designDoc信息不足以准确描述预期输入输出(如字段取值范围、协议格式、历史兼容性约束)时,必须主动查询上述信息源补充,确保测试方案准确。黑盒测试约束:本 Skill 生成的测试方案用于黑盒测试,所有
trigger_condition(输入条件)和expected_behavior(预期输出)中的字段必须是协议中实际存在的字段。禁止构造协议中不存在的字段或假设未定义的嵌套结构。若对字段存在性有疑问,必须查询协议定义文件确认。
Step 2.5: 字段存在性确认(阻塞项)
在生成任何测试场景之前,必须完成以下确认:
1. 字段存在性确认顺序(知识库 → Proto 真相源)
采用两级确认机制,平衡效率与准确性:
Step 1: 查询 LLM-Wiki 知识库(快速索引)
└─ 字段在检索结果中?→ 是 → 直接使用,跳过 proto 搜索
└─ 字段不在检索结果中?→ 否 → 进入 Step 2
Step 2: 搜索 Proto 文件(唯一真相源)
└─ 字段在 proto 中?→ 是 → 新字段,记录并标注 [新增字段: 待 LLM-Wiki 同步]
└─ 字段不在 proto 中?→ 否 → 进入 Step 3
Step 3: 代码 diff 辅助校验
└─ 在 diff 中找到?→ 是 → 可能是内部变量,禁止用于测试
└─ 在 diff 中也找不到?→ 否 → 标注 [待确认: 需开发确认]
LLM-Wiki 知识库检索
服务地址:http://10.190.56.84:8080
Collection:navi-guide-qa-knowledge(需预先创建并上传知识库文档)
检索方式:
POST http://10.190.56.84:8080/search
Content-Type: application/json
{
"collection": "navi-guide-qa-knowledge",
"query": "<feature 关键词 或 字段名>",
"limit": 5
}
检索时机:
- Step 1 字段存在性确认时,用字段名作为 query 快速检索
- 设计测试场景前,用 feature 关键词检索历史测试方案和已知问题
- 遇到边界条件不确定时,检索相关规则和经验
- 确定测试策略时:检索同类功能的 required_phases 决策依据、风险等级评估标准
- 定义数据筛选条件时:检索历史筛选方案(diff / script / reuse)的选择依据和 filter_config 模板
- 配置功能开关时:检索历史 Apollo Key 命名规范、灰度开关配置模式、AB 实验分组规则
- 定义预期差异时:检索历史 diff 预期差异定义格式、unexpected_diff 判定规则
- 设计业务场景时:检索业务规则、播报逻辑、轮次定义、场景触发条件等业务知识(如"播报轮次"、"路口类型"、"距离阈值"等)
检索结果中的
heading和text可用于拼接 RAG 上下文,辅助测试方案设计。 LLM-Wiki 检索结果不能作为字段存在性的最终依据,最终确认仍需搜索 proto 文件。
协议文件(唯一真相源):
导航服务采用 Thrift 定义接口 + Protobuf 定义响应数据结构,需区分搜索:
| 字段类型 | 协议文件 | 搜索目标 |
|---|---|---|
请求字段(如 event_type、route_type) |
.thrift 文件 |
navi_guide_service.thrift 中的 NaviGuideRequest |
响应字段(如 eventType、forkPoint) |
.proto 文件 |
RouteGuidanceInfo、RouteGuidanceInfos 等消息 |
唯一搜索范围:src/navi-guide/src/proto/
该目录下包含所有 Thrift 接口定义和 Protobuf 数据结构定义,不局限于 navi_guide_service.thrift 或 navi_guide_service_apply.proto:
src/navi-guide/src/proto/thrift/*.thrift(13 个文件,含接口、请求/响应结构体)src/navi-guide/src/proto/protobuf/*.proto(8 个文件,含响应数据结构、路线特征、放大图等)其他路径(如
src/ng_diff_new/、src/navi_guide_tools/下的 proto)不得作为字段存在性确认的依据。
搜索策略:
- 先用 grep_code 在 src/navi-guide/src/proto/ 下全局搜索字段名
- 若命中,读取具体文件确认字段所属消息和类型
- 若未命中,再用 search_file 扫描项目内其他 .thrift / .proto 作为兜底
2. 区分请求消息与响应消息
导航服务的协议结构特点:
- 响应消息:通常为 RouteGuidanceInfo(单路径诱导信息)或 RouteGuidanceInfos(多路径诱导信息),包含 forkPoint、multiRoadsForkPoints、eventType、ttsContent 等字段
- 请求字段:请求参数(如 event_type)通常不在单独的 "Request" 消息中定义,而是通过以下方式确认:
- 响应中 eventType 字段的注释为"请求时的event_type",说明它是从请求透传的参数
- 查阅 designDoc 中的请求参数说明
- 查阅历史用例中的请求构造代码
3. 搜索字段的具体操作
对候选字段执行以下验证,根据字段类型选择搜索目标:
A. 请求字段(trigger_condition 中使用)→ 搜索 Thrift:
# 操作 1:在 thrift 目录下全局搜索字段名(精确匹配,区分大小写)
grep_code regex="字段名\s*:" path="src/navi-guide/src/proto/thrift"
# 操作 2:查看命中的文件,确认字段所属结构体
grep_code regex="struct\s+\w+\s*\{" path="<命中文件>" -A 100
B. 响应字段(expected_behavior / affected_fields 中使用)→ 搜索 Proto:
# 操作 1:在 proto 目录下全局搜索字段名(精确匹配,区分大小写)
grep_code regex="字段名\s*=" path="src/navi-guide/src/proto/protobuf"
# 操作 2:查看命中的文件,确认字段所属消息
grep_code regex="message\s+\w+\s*\{" path="<命中文件>" -A 200
# 操作 3:若无精确匹配,尝试模糊搜索
grep_code regex="字段名" path="src/navi-guide/src/proto/protobuf" -i
4. 建立字段来源表
每确认一个字段,记录到来源表:
| 字段名 | 来源(请求/响应) | proto 中的消息 | 确认方式 |
|---|---|---|---|
eventType |
响应(请求透传) | RouteGuidanceInfo |
精确匹配 |
multiRoadsForkPoints |
响应 | RouteGuidanceInfo |
精确匹配 |
5. 代码 diff 辅助校验(解决字段名/关键字精确性)
此步骤利用代码变更内容辅助校验 designDoc 中提到的字段名、日志关键字、Apollo Key 是否与代码实现一致。不是需求融合,仅为命名精确性校验。
当 designDoc 中提到的以下信息需要精确确认时,读取代码 diff 辅助验证:
获取 diff 的方式(按优先级):
1. sourceFiles 参数中的变更文件列表 → 直接读取这些文件的关键代码段
2. 执行 git diff HEAD → 获取完整代码变更
3. 检查 workflowDir/diff-report.md → map-spec code-review 阶段已生成的 diff 报告
diff 辅助校验的应用场景:
| 场景 | 操作 | 示例 |
|---|---|---|
designDoc 写 event_type,不确定 proto 中是 event_type 还是 eventType |
在 diff 中搜索 event_type 和 eventType,看代码中实际使用的字段名 |
diff 中出现 eventType = request->event_type → 确认 proto 中是 eventType |
| designDoc 提到"日志打印 SetmultiRoadsForkPoints" | 在 diff 中搜索 LOG 或 printf 语句,确认日志关键字 exact match |
diff 中出现 LOG(INFO) << "SetmultiRoadsForkPoints event_type=" → 确认关键字 |
| designDoc 提到"灰度开关 fix_eventtype5" | 在 diff 中搜索 apollo 或 GetConfig,确认 Apollo Key 完整名称 |
diff 中出现 GetConfig("fix_eventtype5_parseforkpoint_gray_switch") → 确认 Key |
designDoc 提到新增字段 fork_points |
在 diff 中搜索 fork_points,若代码中只有 forkPoint 和 multiRoadsForkPoints → 禁止虚构 fork_points |
校验规则:
- 若 designDoc 中的字段名/关键字与 diff 中的代码实现不一致,以代码实现为准
- 若 designDoc 中未提及但 diff 中发现新增字段/开关,在测试方案中标注为新增,并纳入字段来源表
- 若 diff 中也找不到对应字段,标注 [待确认: 需开发确认]
6. 字段不在现有来源表中的处理(新字段发现流程)
若搜索到的字段不在当前字段来源表中,说明可能是新引入的字段,执行:
sourceFiles 和 diff 内容,确认该字段是否在本次变更中新增若是已有字段但来源表遗漏 → 补充到来源表
新字段确认流程:
Step A: 在 .proto 中定位字段所属消息(请求/响应)
Step B: 确认字段类型(optional/required/repeated)
Step C: 记录到字段来源表
Step D: 同步到 LLM-Wiki(见下方「新字段知识库同步」)
新字段知识库同步
当 Step 2 发现新字段时,直接通过 /index 同步到 LLM-Wiki:
POST http://10.190.56.84:8080/index
Content-Type: application/json
{
"collection": "navi-guide-qa-knowledge",
"docs": [
{
"id": "new-field-<feature>-<field_name>",
"text": "字段: <field_name>\n类型: <type>\n所属消息: <Message>\n来源: <proto/thrift>\n说明: <简要说明>\n引入功能: <feature>"
}
]
}
如批量发现多个新字段,合并为一个 docs 数组一次性
/index注入。
[新增字段: 待 LLM-Wiki 同步],但不影响测试方案生成7. 标记不确定项:若 PRD 或 designDoc 中提到的概念无法映射到协议字段(如"版本切换"、"已走过的 link"),禁止直接虚构字段名,应:
- 优先寻找协议中可间接表征该状态的字段
- 若找不到,在 trigger_condition 中使用最相关的真实字段(如 eventType == 5),并在说明中备注"版本切换状态通过响应差异反推"
- 绝不能使用 C++ 内部变量名(如 route_has_version_switch、has_traveled_links)作为测试依据
8. 字段命名规范:严格使用 proto 文件中的字段名(区分大小写,如 eventType 而非 event_type)
规则 1:触发条件相同的场景处理
当多个场景的 trigger_condition 完全相同(如都是 event_type == 5),必须采用以下方式之一:
方式 A:合并场景(统计方式)(推荐)
### S001: event_type=5 场景(合并)
- **触发条件**: `event_type == 5`
- **预期分布**:
- 版本切换场景(约 30%):`multiRoadsForkPoints` 有差异(CHANGED)
- 无版本切换场景(约 70%):`multiRoadsForkPoints` 无差异(UNCHANGED)
- **识别方法**: 通过响应中 `multiRoadsForkPoints` 非空识别版本切换
方式 B:区分场景(明确识别方法)
### S001: event_type=5 且发生版本切换
- **触发条件**: `event_type == 5` **且满足以下之一**:
- 响应中 `map_version` 与请求不一致
- 响应中 `multiRoadsForkPoints` 非空(新逻辑生效后)
- **预期行为**: `multiRoadsForkPoints` 包含分歧点(数量 >= 1)
### S002: event_type=5 且无版本切换
- **触发条件**: `event_type == 5` **且**:
- 响应中 `map_version` 与请求一致
- **预期行为**: `multiRoadsForkPoints` 与基线一致(UNCHANGED)
强制要求:若采用方式 B,必须给出明确的识别字段或方法,禁止仅标注"通过响应差异反推"而不说明具体方法。
规则 2:灰度开关场景完整覆盖
若需求提到灰度开关,必须包含以下场景:
| 场景 | 触发条件 | 预期差异 | 优先级 |
|---|---|---|---|
| 灰度关闭 | event_type == 5(灰度开关关闭) |
UNCHANGED(与基线一致) | P0 |
| 灰度开启-生效 | event_type == 5(灰度开关开启,命中白名单) |
CHANGED(新逻辑生效) | P0 |
| 灰度开启-未生效 | event_type == 5(灰度开关开启,未命中白名单) |
UNCHANGED(走老逻辑) | P0 |
规则 3:边界场景系统设计
必须覆盖的边界维度(根据业务逻辑选择至少 2 个维度,每个维度至少 2 个边界点):
| 维度 | 边界点 | 示例场景 |
|---|---|---|
| 时间/进度边界 | 刚触发、触发后一段时间、长期 | 刚版本切换、切换后 10km、切换后到达终点 |
| 空间边界 | 起点、中途、终点 | 起点 0km、中途 50%、终点前 100m |
| 数量边界 | 0、1、多个 | 无分歧点、1 个分歧点、多个分歧点 |
| 状态边界 | 正常、异常、极限 | 正常版本切换、切换失败、多次切换 |
基于协议中实际存在的可观测字段设计测试场景,每个场景必须包含:
| 字段 | 说明 | 字段来源限制 |
|---|---|---|
scenario_id |
唯一标识,如 S001 |
- |
scenario_name |
场景名称,简明描述测试意图 | - |
trigger_condition |
触发条件(协议字段 + 阈值) | 只能使用请求协议中的字段 |
expected_behavior |
预期行为(具体字段值变化) | 只能使用响应协议中的字段 |
affected_fields |
受影响的可观测字段列表 | 只能使用响应协议中的字段 |
test_type |
diff / functional / boundary |
- |
验证方式 |
协议字段 / 日志 / 协议+日志 |
若选日志或协议+日志,日志关键字必须来自源码确认 |
priority |
P0 / P1 / P2 |
- |
字段使用规则:
- trigger_condition 中的字段必须是请求消息(Request)中实际存在的字段
- expected_behavior 和 affected_fields 中的字段必须是响应消息(Response)中实际存在的字段
- 禁止将 C++ 内部变量、临时计算量、未序列化的内存对象作为测试依据
- 若需求描述中的概念无法直接映射到协议字段,应使用最接近的真实字段,并在场景说明中解释映射关系
示例场景:
### S001: F 路口提前播报
- **触发条件**: `scene_type == F_FORK && distance_to_fork <= 150m`
- **预期行为**: `ttsContent` 包含 "前方路口",提前 50m 播报
- **受影响字段**: `ttsContent`, `voiceType`
- **测试类型**: diff
- **优先级**: P0
为每个 diff 类型场景定义数据筛选条件:
| 条件维度 | 示例 |
|---|---|
scene_type |
F_FORK, T_JUNCTION |
distance_range |
100m ~ 200m |
version |
>= 490 |
city_list |
北京、上海、深圳 |
time_range |
高峰时段覆盖 |
筛选条件字段限制:
- 筛选条件只能使用请求协议中实际存在的字段
- 禁止用响应字段、C++ 内部变量或 AI 推导的抽象概念作为筛选维度
- 若需求涉及无法直接筛选的内部状态(如"版本切换"、"已走过的 link"),应:
1. 先用请求中可筛选的字段缩小范围(如 eventType == 5)
2. 再通过 diff 输出的响应差异来识别目标场景
3. 在 说明 列中标注"内部状态通过响应差异反推,非直接筛选"
生成 data_filter_conditions 字段,供 search-data-script 使用。
根据以下因素评估:
| 因素 | 高风险表现 |
|---|---|
| 改动范围 | 核心算法、多模块联动 |
| 影响面 | 全量用户、全场景 |
| 字段类型 | 语音播报、安全相关事件 |
| 历史稳定性 | 同类改动曾引发线上问题 |
风险等级与 required_phases 映射:
| 风险等级 | 说明 | required_phases |
|---|---|---|
low |
纯配置/文案变更,影响面极小 | [1, 2, 3, 4] |
medium |
单一场景优化,影响面可控 | [1, 2, 3, 4, 5, 6] |
high |
核心算法变更,多场景联动 | [1, 2, 3, 4, 5, 6] |
Phase 6(汇总报告)为必须执行阶段,用于汇总各 Phase 产物生成最终报告。
测试策略建议(写入 test_plan.md "测试策略"章节):
| 变更类型 | Diff | 功能 Case | 回归 | 说明 |
|---|---|---|---|---|
| 纯配置/文案调整 | ✅ | 必须 | ❌ | Case 生成验证配置生效 |
| 新功能/新场景 | ✅ | 必须 | ✅ | |
| 算法优化 | ✅ | 必须 | ✅ | diff 覆盖核心差异,Case 验证边界 |
| 逻辑修复/Bugfix | ✅ | 必须 | ✅ | Case 验证边界场景 |
| 接口变更 | ✅ | 必须 | ✅ |
功能 Case 为必须执行,用于验证边界场景和补充 diff 无法覆盖的功能点。压测暂不执行。
逻辑修复特殊说明:若修复的是特定场景下的异常处理逻辑(如本例的 event-type=5 版本切换),功能 Case 用于验证边界场景(部分走过、全走过),压测通常不需要。需在"判断依据"中明确说明为何选择该策略。
AB 实验特殊策略:
- Diff 需跑两组:实验组(基线 vs 实验组)+ 对照组(基线 vs 对照组)
- 实验组预期:有差异(符合 test_plan 预期)
- 对照组预期:无差异(对照组应与基线一致)
- 对照组白名单:对照组可能使用独立白名单(driver_id 非空),也可能与非白名单共用默认逻辑(driver_id=""),视实验设计而定
- 若对照组出现差异 → 实验分流逻辑有问题,需标记为 bug
输出路径:<CASE_DIR>/<feature>/test_plan.md
按 test-plan-template-v2.md 模板格式生成,确保包含以下必填字段:
- feature:功能名
- 测试执行方式:列出 Jenkins MCP 调度的流水线(diff 流水线 / 回归流水线 / 压测流水线),标注是否使用
- test_scenarios:测试场景列表(每个场景含 scenario_id / trigger_condition / expected_behavior / affected_fields / test_type / 验证方式 / priority)
- 验证方式:协议字段(仅 diff 验证) / 日志(仅日志验证) / 协议+日志(两者结合)
- data_filter_conditions:数据筛选条件(供 search-data 使用)
- 功能开关控制:开关类型、白名单、实验组等信息
- 测试策略:Diff / 功能 Case / 回归 / 压测 的是否需要执行
- experiment_config:实验/灰度配置(决定 diff 执行次数和分组)
json
{
"type": "ab_test",
"groups": [
{"name": "control", "driver_id": "202606101119", "desc": "对照组(白名单A)"},
{"name": "treatment", "driver_id": "580548822047701", "desc": "实验组(白名单B)"}
]
}
- type:none(无实验)/ ab_test / grayscale
- groups:diff 分组列表,每组含 name、driver_id、desc
- driver_id:该组的白名单 ID。空字符串表示非白名单(走默认逻辑),非空字符串表示使用该白名单
- 对照组可能有自己的白名单(如对照组白名单A),也可能不设白名单(空字符串,与非白名单共用默认逻辑)
- 由功能开关控制信息自动推导:若开关类型为 AB实验/灰度,则 type 对应设置,groups 按对照组/实验组或白名单/非白名单拆分
- diff_config:Diff 执行配置与预期结果(输入不可预期,但输出必须可预期)
- 对比配置:基线/目标服务地址(对比字段、忽略字段、请求数据来源等由 ng_diff_new 内部实现决定,测试方案仅指定 host)
- 预期差异结果(Expected Diff):每个场景下哪些字段、基线值→目标值、变化方向(CHANGED/ADDED/REMOVED/UNCHANGED)。注意:diff 输入为线上真实流量,无法预先控制哪些请求会被打到,上表定义的是"如果某个请求的响应中该字段出现差异,则差异应符合上述定义"
- 分组预期差异:按 experiment_config.groups 中的 name 分组定义预期差异,格式如下:
markdown
| 分组 | 场景ID | 字段 | 基线值 | 目标值 | 变化方向 |
|------|--------|------|--------|--------|----------|
| treatment | S001 | ttsContent | - | 包含"前方路口" | CHANGED |
| treatment | S001 | multiRoadsForkPoints | 空 | 数量>=1 | ADDED |
| control | S001 | ttsContent | - | - | UNCHANGED |
| control | S001 | multiRoadsForkPoints | - | - | UNCHANGED |
- 实验组/白名单组:按正常场景预期填写(CHANGED / ADDED / REMOVED)
- 对照组/非白名单组:全部为 UNCHANGED,与基线一致。若对照组出现任何差异 → 实验分流逻辑有 bug(对照组无论是否有独立白名单,都应与基线一致)
- 非预期差异判定规则:哪些情况属于 unexpected_diff,供 Phase 2 diff-executor 使用
- 日志验证(如适用):哪些场景需结合日志验证、日志关键字/指标、预期结果
- required_phases:需要执行的 Phase 列表
- risk_level:风险等级(low/medium/high)
生成过程中的强制校验:
每生成一个场景,立即执行:
1. 字段来源校验:trigger_condition 中的字段是否都在请求 proto 中?expected_behavior / affected_fields 中的字段是否都在响应 proto 中?
2. 字段命名校验:字段名是否与 proto 中完全一致(区分大小写)?
3. 任一校验失败,禁止继续生成,必须先修正字段
待确认项标注规范:
以下信息若无法从现有文档/代码中直接确认,必须标注 [待确认: 需开发确认],禁止虚构:
| 信息项 | 示例 | 标注方式 |
|---|---|---|
| Apollo Key | fix_eventtype5_parseforkpoint_gray_switch |
[待确认: 需开发确认] Apollo Key |
| 日志关键字 | SetmultiRoadsForkPoints event_type=5 |
[待确认: 需开发确认] 日志关键字 |
| 白名单内容 | 具体城市/用户群体 | [待确认: 需开发确认] 白名单 |
| 字段存在性 | 不确定某字段是否在协议中 | 必须先查询 proto 确认,不能标注待确认 |
日志关键字获取方式:优先读取
sourceFiles中的源码,搜索LOG/WARN/ERROR/INFO等日志输出语句,提取实际日志格式。若源码不可读或未找到相关日志,则标注[待确认: 需开发确认]。
模板文件位置:testing/test-plan-writer/test-plan-template.md
生成完成后,AI 自检以下项。以下检查项为阻塞项,任一项未通过必须修正后方可输出:
feature 字段与目录名一致test_scenarios 非空,每个场景有完整字段data_filter_conditions 与 test_scenarios 一一对应data_filter_conditions 中的字段均为请求协议中实际存在的字段(无 C++ 内部变量)功能开关控制 完整(开关类型、白名单/实验组信息)测试策略 已根据变更类型自动判断(Diff / 功能 Case / 回归 / 压测),且判断依据明确required_phases 与测试策略一致risk_level 评估有依据diff_config 中每个 diff 类型场景都有对应的预期差异定义(含场景ID、字段、基线值、目标值、变化方向)UNCHANGED(与基线一致)sourceFiles 非空或 git diff HEAD 可执行,已从 diff 中提取实际日志关键字和 Apollo Key;禁止在已执行 diff 分析的情况下仍标注 [待确认: 需开发确认]expected_behavior 包含具体数值、状态或范围;无"正常"、"有效"、"正确"等模糊描述;若无法量化,说明中已写明量化困难原因和替代验证方式trigger_condition 中无无法从请求字段推断的内部状态trigger_condition 中的字段均在请求协议中存在;expected_behavior / affected_fields 中的字段均在响应协议中存在;无虚构字段;字段名大小写与 proto 一致[待确认: 需开发确认] 或已读取源码/diff 确认[待确认: 需开发确认] 或已从 diff 中提取实际值/index 同步到 LLM-Wiki navi-guide-qa-knowledge(或已标注 [新增字段: 待 LLM-Wiki 同步])自检失败处理流程: 1. 标记失败的检查项 2. 返回对应 Step 修正(字段问题 → Step 2.5/Step 3,日志问题 → 读取源码或标注待确认) 3. 重新执行自检,直至全部通过
执行日志(必须包含在输出中,便于审计):
## 执行日志
### 检查清单 A:代码 diff 分析
- [✅/❌/⚠️] 读取 sourceFiles:`ng_crossfinder_strategy.cpp`
- [✅/❌/⚠️] 搜索日志关键字:找到 `LOG(INFO) << "SetmultiRoadsForkPoints event_type="`
- [✅/❌/⚠️] 搜索 Apollo Key:找到 `GetConfig("fix_eventtype5_parseforkpoint_gray_switch")`
- [✅/❌/⚠️] 搜索分支逻辑:找到 `if (event_type == 5)`
### 检查清单 B:字段存在性确认
- [✅] Thrift 搜索 `event_type`:找到 `NaviGuideRequest` line 332
- [✅] Proto 搜索 `eventType`:找到 `RouteGuidanceInfo` line 1617
- [✅] Proto 搜索 `multiRoadsForkPoints`:找到 `RouteGuidanceInfo` line 1610
- [✅] 建立字段来源表:已完成(4 个字段全部确认)
### 检查清单 C:预期结果量化
- [✅] S001 expected_behavior:`multiRoadsForkPoints.size() >= 1`(已量化)
- [✅] S002 expected_behavior:`eventType == 5`(已量化)
- [⚠️] S003 expected_behavior:`与基线一致`(降级为警告,原因:回归场景无具体数值可量化)
### 场景细分检查
- [✅] 触发条件相同处理:S001-S005 的 trigger_condition 均为 `event_type == 5`,已合并为统计方式
- [✅] 灰度开关覆盖:已覆盖关闭/开启生效/开启未生效 3 个场景
- [✅] 边界维度:覆盖数量边界(0/1/多个)和空间边界(起点/中途/终点)
### 质量门禁
- [✅] 待确认项数量:2 项(白名单内容、日志关键字精确格式)
- [✅] 模糊描述检查:无模糊词
- [✅] 字段存在性:无虚构字段
作用:便于用户检查 AI 是否真的执行了关键步骤,便于事后审计和改进。
输出摘要(告知用户,无需确认):
测试方案已生成:
【功能概述】<简述>
【测试场景】共 X 个场景(P0: a个, P1: b个, P2: c个)
【功能开关】<开关类型> / <Apollo Key>
【测试策略】(已自动判断)
- Diff 对比: 是/否
- 功能 Case: **必须执行**
- 回归测试: 是/否
【风险等级】<low/medium/high>
【执行阶段】required_phases = [...]
任一自检失败,重新生成并标注修正点。
生成完成后,自检以下项:
feature 字段与目录名一致test_scenarios 非空,每个场景有完整字段data_filter_conditions 与 test_scenarios 一一对应data_filter_conditions 中的筛选字段均为请求协议中实际存在的字段required_phases 包含 Phase 1(自身)risk_level 评估有依据(引用改动范围或影响面)筛选映射表 中每条预期差异都有场景对应trigger_condition 字段均为请求协议字段,expected_behavior / affected_fields 字段均为响应协议字段小葱技能站7w4.net每天更新,海量AI技能等你发现。
[待确认: 需开发确认]校验失败处理: 1. 标注具体失败项和原因 2. 返回对应 Step 修正 3. 重新校验直至全部通过
| 产物 | 路径 |
|---|---|
| 测试方案 | <CASE_DIR>/<feature>/test_plan.md |
这个 Skill 质量很好,文档详细、逻辑清晰、使用方便。做得好的地方:步骤流程完整、检查清单详细、有现成的模板和示例;做了很多质量把关措施,能避免常见错误。不足之处:依赖内部服务地址、不方便在外部使用;有些细节说明分散在多处,查找不够方便。总体来说,这是一个专业度高、实用性强的 Skill。