feat(tester): add client journey testing standard

This commit is contained in:
2026-10-15 16:48:54 +08:00
parent baed23d02a
commit ffa7cbd63e
12 changed files with 518 additions and 0 deletions
+18
View File
@@ -40,6 +40,11 @@ catalog(整个测试集)
| `how` | 怎么测:`steps` / `run` / `url` / `timeout` | test steps | 不是判定标准 |
| `expected` | 怎么算过:`{kind, value}` 列表 | pytest `assert`;Robot `Should …` | 不是步骤 |
| `run_together` | 必须成组执行的用例(有序、可选 continue_on_fail) | 场景组(scenario) | 不是 suite,不是 tag |
| `client_suite` | 客户端用户旅程套件:`client-smoke` / `client-regression` / `safety` | mobile/browser E2E suite | 不是 `category`,也不是通用 tag;迁移期可读 `suite` 别名 |
| `journey` | 用户目标、入口、操作、UI 断言、最终状态和失败继续策略 | Cucumber scenario / Robot task | 不是 API contract |
| `fixture` | 旅程开始前声明的测试账号与隔离数据 | pytest fixture | 不是运行中临时猜出的数据 |
| `cleanup` | 本轮数据的 teardown 契约;缺失会在 preflight 阻断 | pytest teardown / Robot teardown | 不是测试通过后的可选备注 |
| `platform` | 旅程适用的 `android`/`ios` 平台列表 | device matrix | 不是 tester Agent binding;binding 仍在命令参数/运行环境 |
### 结果词
@@ -99,6 +104,19 @@ catalog(整个测试集)
- 因此它是**分类级前置条件**:`catalog.status_probe` 是全局默认,`categories[].status_probe` 可覆盖(缺省继承 / `false` 关闭 / mapping 整体替换)。对应 pytest 的 session fixture、Robot 的 Suite Setup。
- 命中或不可达只影响用它的分类;只有所有选中分类都被挡住,整轮结论才是 `environment-fail`。`select` 会按分类返回解析后的生效值,Agent 不要自己读 catalog 猜。
### 3.5 客户端套件和生命周期
客户端套件固定为:
- `client-smoke`:启动、正确账号登录和少量只读主路径,通常配 `tag=smoke`、`risk=readonly`。
- `client-regression`:完整的普通用户旅程,可包含隔离环境中的 `mutating` 操作;不等于所有 full 用例。
- `safety`:删除、权限边界、改密、停用或故障注入等危险旅程;必须显式选择和授权,不进入默认运行。
每条旅程按 `preflight → 启动 → 登录 → 旅程 → 断言 → 清理 → 报告` 执行。登录失败在
客户端和正确测试账号都已就绪时记 `fail`;设备、产物、服务或 session 不可达才记
`environment-fail`。fixture/cleanup 缺失是 contract block,不伪装成逐条结果。前置失败
后的剩余条目是 `not-run`,主动排除仍是 `skipped`。
## 4. 不采用的词
| 词 | 不用它的原因 |
+9
View File
@@ -81,6 +81,15 @@ python3 <skill-dir>/scripts/catalog.py select --root <root> \
[--binding android=tester.android] [--binding ios=tester.ios]
```
客户端旅程还可按标准套件选择:
```bash
python3 <skill-dir>/scripts/catalog.py select --root <root> --suite client-smoke
```
`client-smoke`、`client-regression`、`safety` 是 `client_suite` 的固定值,不替换既有
`tag=smoke|full|slow`。`safety` 不参与未指定范围的默认选择。
选中任一 `driver=mobile` 用例时,`--platform`、每个平台的 `--artifact` 和对应
`tester` binding 是硬性参数;`catalog.py select` 会检查产物文件/下载地址和
`.pouch/agent.yaml` 中的 binding。缺任何一项直接返回非零并打印 `preflight: blocked`,
+50
View File
@@ -14,6 +14,11 @@ runs/<run-id>.yaml 每次运行快照(执行源);run-id 必须唯一
runs/*.csv 同一次运行的成绩单;generate_run_csv.py 从对应 yaml 生成
```
客户端旅程在上述目录中仍使用同一套 cases/runs 和五态报告,不另造成绩单。启用旅程
契约的用例声明 `client_suite`(兼容迁移期别名 `suite`);catalog 可用
`client_suites` 显式列出三个标准套件:`client-smoke`、`client-regression`、`safety`。
`category` 仍是文件/执行顺序容器,不被套件字段替代。
## catalog.yaml
| 字段 | 含义 |
@@ -29,6 +34,7 @@ runs/*.csv 同一次运行的成绩单;generate_run_csv.py 从对
| `id_pattern` | 用例 id 正则 |
| `run_together` | 可选。成组执行:`[{ids: [TC-…, TC-…], continue_on_fail: true}]`。点到组内任一条,`select` 会按 `ids` 顺序把整组扩进命中列表。`continue_on_fail: true` 时组内前一条产品 fail 仍跑后一条(返回/收尾)。一个 id 只能属于一组。 |
| `status_probe` | 可选。顶层默认值,一个**分类级前置条件**:`{url, stop_if: {字段: [命中值]}}`。运行前请求 `url`,响应命中任一 `stop_if` 条件即视为前置不满足。分类项可用同名键覆盖:缺省继承顶层、`false` 关闭、mapping 整体替换。命中或不可达只影响用它的分类,不是产品失败。业界对应 pytest 的 session fixture / Robot 的 Suite Setup。`select` 会按分类返回解析后的生效值,`url` 已用 `vars` 展开 |
| `client_suites` | 可选的标准套件声明;出现时必须完整包含 `client-smoke`、`client-regression`、`safety`。缺省运行仍按 `tag` 兼容旧目录 |
`local.yaml` 与环境变量覆盖 `HOST` 和授权开关。模板中的 `HOST` 默认是
`127.0.0.1`;远程目标必须显式覆盖。查找顺序:环境变量 > `local.yaml` >
@@ -88,6 +94,50 @@ terminal;运行结束用 `orca terminal close --terminal <handle>` 关闭本
| `teardown` | 否 | 跑完回退 |
| `notes` | 否 | 备注 |
### 客户端旅程用例契约
声明 `client_suite` 后,以下字段都必须存在;`catalog.py validate` 和 `select` 使用同一
套检查。契约缺失是 `preflight: blocked`,不生成逐条产品失败。
| 字段 | 必填 | 约束 |
|------|------|------|
| `client_suite` | 是 | `client-smoke`、`client-regression` 或 `safety`;安全套件必须显式选择,不属于默认 smoke/full |
| `journey.goal` | 是 | 用户目标,不写接口目标 |
| `journey.entry_state` | 是 | 启动/登录前的可观察入口状态 |
| `journey.actions` | 是 | 非空用户操作列表;真实 App/browser 驱动从这里开始 |
| `journey.assertions` | 是 | 非空可观察 UI 断言列表;最终业务状态可另由 `expected` 的 api-field/api-status 佐证 |
| `journey.final_state` | 是 | 旅程完成后的业务状态 |
| `journey.failure_policy` | 是 | `stop` 或 `continue`;默认不隐式吞掉前置失败 |
| `fixture` | 是 | mapping,至少有 `account`;账号和数据由 app catalog 提供,skill 不写具体账号 |
| `session` | 是 | 复用的 browser/mobile session 名,必须在 catalog.sessions 声明 |
| `cleanup` | 是 | mapping,必须有非空 `steps`/`run`;只读旅程也要用 `required: false` + `reason` 显式说明无需清理 |
| `platform` | 是 | `android`/`ios` 的字符串或列表,不能重复;平台差异只能进入执行适配层 |
一个最小形状如下(值是占位符,不是 app 规则):
```yaml
client_suite: client-smoke
platform: [android, ios]
journey:
goal: "用户完成一个可观察的客户端目标"
entry_state: logged_out
actions: ["启动客户端", "输入测试账号并登录", "完成目标操作"]
assertions: ["看到目标页面", "看到成功提示"]
final_state: "目标状态已保存"
failure_policy: stop
fixture:
account: test-role
data: []
session: mobile
cleanup:
steps: ["删除本轮 run-id 创建的数据"]
```
账号登录属于 session 初始化:正确测试账号登录失败是产品 `fail`;设备、产物或服务
不可达才是 `environment-fail`。fixture/cleanup 缺失必须在启动、登录和任何旅程操作前
阻断。旅程失败后,尚未轮到的用例记 `not-run`;风险、生命周期或明确范围排除记
`skipped`。清理失败单独写入运行快照的 cleanup 证据,不改造为一个新的 result 值。
## how
按 `driver` 分派;`automation=manual` 时没有 `driver`,只有 `how.steps`(人工步骤)。