继续完善测试模板
This commit is contained in:
@@ -6,6 +6,43 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 我该把测试写在哪里?
|
||||||
|
|
||||||
|
人类开发者与 Cursor 等 AI 工具在加用例前,先对照下表决定**文件落点**与**是否默认执行**:
|
||||||
|
|
||||||
|
| 场景 | 放在哪里 | 默认是否运行 | 说明 |
|
||||||
|
|------|----------|--------------|------|
|
||||||
|
| CLI / health / version / metadata / runtime 路径 | `tests/test_*.py` | 是 | 默认必跑,保持轻量、无外联 |
|
||||||
|
| 纯函数 / 业务规则 | `tests/test_*.py` | 是 | 不依赖真实外部系统 |
|
||||||
|
| service 层契约 | 从 `tests/samples/test_service_contract.py.sample` 复制到 `tests/test_service_contract.py` | 是,复制后 | 用 `FakeAdapter` / stub 注入外部依赖 |
|
||||||
|
| golden fixture 回归 | 从 `tests/samples/test_golden_cases.py.sample` 复制到 `tests/test_golden_cases.py` | 是,复制后 | 输入样例 + 期望输出,适合解析、计算、校验类技能 |
|
||||||
|
| 仿真 API | `tests/integration/test_simulator_api_contract.py.sample` | 否 | 复制并设置 `OPENCLAW_TEST_TARGET=simulator_api`(或兼容键名 `OPENCLOW_TEST_TARGET`)后按需运行 |
|
||||||
|
| 仿真 RPA / 页面操作 | `tests/integration/test_simulator_rpa_contract.py.sample` | 否 | 用 localhost / 仿真页面,不碰真实系统 |
|
||||||
|
| 真实 API | `tests/integration/test_real_api_contract.py.sample` | 否 | 必须 `OPENCLAW_TEST_TARGET=real_api` 且 `ALLOW_REAL_API=1` |
|
||||||
|
| 真实 RPA | `tests/integration/test_real_rpa_contract.py.sample` | 否 | 最高风险,只能手动触发 |
|
||||||
|
| 桌面宿主 E2E | `tests/desktop/test_desktop_smoke.py.sample` | 否 | 需要 pytest + `jiangchang_desktop_sdk` |
|
||||||
|
|
||||||
|
说明:`python tests/run_tests.py` **只**收集 `tests/` **根目录**下的 `test_*.py`;子目录里的 `*.sample` 与 desktop 用例**不会**被默认发现。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 新技能最小测试清单
|
||||||
|
|
||||||
|
从本模板复制出新技能仓库后,建议至少逐项确认:
|
||||||
|
|
||||||
|
- [ ] `python tests/run_tests.py -v` 能通过。
|
||||||
|
- [ ] `python scripts/main.py health` 能通过。
|
||||||
|
- [ ] `python scripts/main.py version` 输出 JSON,且 `skill` 与目录名 / `SKILL.md` / `constants.SKILL_SLUG` 一致。
|
||||||
|
- [ ] 所有 DB / 文件写入都在 `IsolatedDataRoot`(或等价隔离)下测试,不写真实数据目录。
|
||||||
|
- [ ] 外部系统默认使用 mock / `FakeAdapter`,不访问真实 API,不打开真实 RPA。
|
||||||
|
- [ ] 至少有 1 个成功路径测试。
|
||||||
|
- [ ] 至少有 1 个缺必填字段 / 非法输入测试。
|
||||||
|
- [ ] 如果有 adapter,至少覆盖 timeout / unauthorized / invalid response(可用 fake 模拟)。
|
||||||
|
- [ ] 如果有解析 / 计算 / 校验类业务,至少保留 1 组 golden fixture(脱敏)。
|
||||||
|
- [ ] 真实 API / 真实 RPA 测试只放 `tests/integration/`,并且默认 `.sample`,不默认运行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 1. 默认必跑(unittest)
|
## 1. 默认必跑(unittest)
|
||||||
|
|
||||||
在**技能仓库根目录**执行:
|
在**技能仓库根目录**执行:
|
||||||
@@ -113,6 +150,21 @@ python tests/run_tests.py test_cli_smoke
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## AI 编程工具注意事项(Cursor 等)
|
||||||
|
|
||||||
|
以下约束适用于为本仓库或复制出的技能编写/修改测试时,**避免误伤业务与生产**:
|
||||||
|
|
||||||
|
- **不要为了**让测试通过去改业务代码,除非用户明确要求修改实现。
|
||||||
|
- **不要**把真实外部系统调用写进默认的 `tests/test_*.py`(默认套件须无外联、无真实浏览器)。
|
||||||
|
- **不要**把 `*.sample` 改名为可执行测试或移入根目录并去掉 `.sample`,除非用户明确要求启用 integration / 真实联调。
|
||||||
|
- **不要**硬编码真实凭证、token、cookie、生产 URL;占位请用明显虚构域名(如 `example.invalid`)或文档约定的 `credential_ref`。
|
||||||
|
- 默认测试只能使用 **mock、stub、`FakeAdapter`、fixtures、隔离 SQLite** 等安全手段。
|
||||||
|
- 业务逻辑应优先在 **service 层** 编写与测试,不要只断言 CLI 文案。
|
||||||
|
- 异常路径应返回**结构化错误**(如统一错误码/字段),避免裸异常直接穿透到 CLI。
|
||||||
|
- 有真实系统联调需求时,测试应写在 `tests/integration/`,并配合 `OPENCLAW_TEST_TARGET` / `ALLOW_REAL_*` / `ALLOW_WRITE_ACTIONS` 等**显式开关**;不得默认开启。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 6. 与 content-manager 的关系
|
## 6. 与 content-manager 的关系
|
||||||
|
|
||||||
- **未**复制 article / media / prompt / NotebookLM 等业务测试。
|
- **未**复制 article / media / prompt / NotebookLM 等业务测试。
|
||||||
|
|||||||
@@ -30,3 +30,5 @@ pytest tests/desktop/test_desktop_smoke.py -v -s
|
|||||||
|
|
||||||
- **后端 / CLI / DB**:请使用仓库根目录下 `python tests/run_tests.py`(见 `tests/README.md`)。
|
- **后端 / CLI / DB**:请使用仓库根目录下 `python tests/run_tests.py`(见 `tests/README.md`)。
|
||||||
- **桌面 E2E**:仅在本目录用 **pytest** 维护,二者互补、互不替代。
|
- **桌面 E2E**:仅在本目录用 **pytest** 维护,二者互补、互不替代。
|
||||||
|
|
||||||
|
若仅需验证 **service 业务逻辑**、契约与 golden 回归,**不要**写 desktop E2E;desktop 只用于验证**宿主 UI、路由、`jiangchang://` 协议、真实用户交互链路**等与桌面宿主强相关的行为。
|
||||||
|
|||||||
10
tests/fixtures/README.md
vendored
10
tests/fixtures/README.md
vendored
@@ -15,3 +15,13 @@
|
|||||||
| `sample_request.json` | 通用请求体样例,供 ``tests/samples/test_golden_cases.py.sample`` 演示读取。 |
|
| `sample_request.json` | 通用请求体样例,供 ``tests/samples/test_golden_cases.py.sample`` 演示读取。 |
|
||||||
| `expected_response.json` | 与上配套的期望摘要字段(业务复制后按域替换)。 |
|
| `expected_response.json` | 与上配套的期望摘要字段(业务复制后按域替换)。 |
|
||||||
| `adapter_profiles.sample.yaml` | 五类运行方式(mock / simulator_api / simulator_rpa / real_api / real_rpa)的配置表达样例。 |
|
| `adapter_profiles.sample.yaml` | 五类运行方式(mock / simulator_api / simulator_rpa / real_api / real_rpa)的配置表达样例。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 给 AI 工具的替换规则
|
||||||
|
|
||||||
|
- 可以按业务**重命名字段**、增减键名,但必须保持**虚构、脱敏**;不得把真实客户标识原样搬进仓库。
|
||||||
|
- 可以**新增**更多 fixture 文件,例如 `case_valid_001.json`、`case_missing_required_001.json`,并在测试中通过相对路径读取。
|
||||||
|
- **禁止**将真实企业数据、真实单证截图中的文字、真实报关要素未经处理直接复制进 `fixtures/`。
|
||||||
|
- 若用例来自真实客户样本,须先**脱敏、泛化、替换编号与地名**,再作为模板数据提交。
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,16 @@
|
|||||||
- ``python tests/run_tests.py`` 只收集 ``tests/`` 根目录下 ``test_*.py``,**不会**进入本目录。
|
- ``python tests/run_tests.py`` 只收集 ``tests/`` 根目录下 ``test_*.py``,**不会**进入本目录。
|
||||||
- 本目录下文件一律为 ``*.py.sample``,pytest / unittest **默认也不会**按普通 ``test_*.py`` 收集。
|
- 本目录下文件一律为 ``*.py.sample``,pytest / unittest **默认也不会**按普通 ``test_*.py`` 收集。
|
||||||
|
|
||||||
|
## 与 samples 的区别
|
||||||
|
|
||||||
|
| 维度 | `tests/samples/` | `tests/integration/` |
|
||||||
|
|------|------------------|------------------------|
|
||||||
|
| 目的 | 默认可**升级**为根目录 `test_*.py` 的 **业务单元 / service 契约 / golden** 范式 | **外联联调**范式:网络、浏览器、仿真或真实系统 |
|
||||||
|
| 默认执行 | `.sample` 不执行;复制到根目录并改名后才进入默认套件 | **永远不**作为默认 unittest 的一部分;复制后仍需显式环境变量与手动/CI 任务触发 |
|
||||||
|
| 典型内容 | `FakeAdapter`、fixture、隔离 DB | `localhost` 仿真、真实 API(授权)、真实 RPA(最高风险) |
|
||||||
|
|
||||||
|
**原则**:只要会访问**网络**、**浏览器**、**真实或仿真外部系统**,应优先放在 `integration`(并保持 `.sample` 直至团队同意启用),而不是写进默认 `tests/test_*.py`。
|
||||||
|
|
||||||
## 何时使用
|
## 何时使用
|
||||||
|
|
||||||
| 场景 | 建议 ``OPENCLAW_TEST_TARGET`` | 其它条件 |
|
| 场景 | 建议 ``OPENCLAW_TEST_TARGET`` | 其它条件 |
|
||||||
|
|||||||
@@ -1,5 +1,112 @@
|
|||||||
# samples(样例,默认不执行)
|
# samples(业务测试样例,默认不执行)
|
||||||
|
|
||||||
本目录下的 ``*.py.sample`` **不会被** ``python tests/run_tests.py`` 收集。
|
本目录存放**可复制到根目录的业务测试范式**,面向「企业数字员工技能」中 **ERP、TMS、WMS、船司、报关、邮件、LLM、RPA** 等外联场景下的 **service / golden** 测法说明。
|
||||||
|
|
||||||
复制为 ``tests/test_*.py``(去掉 ``.sample``)后,再按业务实现 service / golden 断言。
|
---
|
||||||
|
|
||||||
|
## 作用
|
||||||
|
|
||||||
|
- 这里的文件是 **`.py.sample`**:`python tests/run_tests.py` **不会**收集本目录(`run_tests.py` 只发现 `tests/` 根目录下的 `test_*.py`)。
|
||||||
|
- 人类或 AI 需要启用范式时:**复制**到 `tests/test_<名称>.py`(去掉 `.sample`),再按业务改 import、断言与 fixture 路径。
|
||||||
|
- **禁止**把真实联调、真实网络、真实 RPA 写进复制后的默认用例;此类内容应放在 `tests/integration/*.sample` 并配合环境变量开关。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 两类样例文件
|
||||||
|
|
||||||
|
### 1. `test_service_contract.py.sample`
|
||||||
|
|
||||||
|
**适合:**
|
||||||
|
|
||||||
|
- 技能有清晰的 **service 层入口**,例如 `run(...)`、`handle(...)`、`execute(...)`。
|
||||||
|
- 需要验证:**成功路径**、**参数/校验错误**、**adapter 异常被转换为业务可读错误**。
|
||||||
|
- 需要用 **`FakeAdapter`**(见 `tests/adapter_test_utils.py`)或自建 **stub** 替代 ERP、TMS、WMS、船司、报关、LLM、RPA 等,**不**发真实 HTTP、**不**开真实浏览器。
|
||||||
|
|
||||||
|
### 2. `test_golden_cases.py.sample`
|
||||||
|
|
||||||
|
**适合:**
|
||||||
|
|
||||||
|
- **解析类**:邮件/单证/PDF/Excel 等结构化输出。
|
||||||
|
- **计算类**:费用、税费、分摊、账单字段。
|
||||||
|
- **校验类**:合规、字段一致性、状态机规则。
|
||||||
|
|
||||||
|
推荐配合根目录下的 **`tests/fixtures/sample_request.json`** 与 **`tests/fixtures/expected_response.json`**(复制技能后按域替换字段,保持脱敏)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 如何启用 service contract 样例
|
||||||
|
|
||||||
|
1. **复制文件**
|
||||||
|
`tests/samples/test_service_contract.py.sample`
|
||||||
|
→ `tests/test_service_contract.py`(或 `tests/test_<domain>_service_contract.py`,须符合根目录 `test_*.py` 命名以便 `run_tests.py` 收集)。
|
||||||
|
|
||||||
|
2. **替换示例 import**
|
||||||
|
把注释中的示意:
|
||||||
|
`# from service.your_service import run`
|
||||||
|
改为真实模块,例如:
|
||||||
|
`from service.quote_service import run_quote`
|
||||||
|
(具体路径以技能代码为准。)
|
||||||
|
|
||||||
|
3. **注入假外部依赖**
|
||||||
|
- **不要**在默认用例里直接 `requests` 访问 ERP/TMS/WMS 等。
|
||||||
|
- **不要**打开真实网页或驱动真实 RPA。
|
||||||
|
- **不要**调用真实 LLM;可 stub 返回固定 JSON。
|
||||||
|
- 对 **timeout / unauthorized / invalid_response** 等分支,用 `FakeAdapter` 或自建 fake 模拟(与 `adapter_test_utils` 思路一致)。
|
||||||
|
|
||||||
|
4. **至少保留三类断言习惯**
|
||||||
|
- **成功路径**:返回 `success: true` 或与技能约定的成功结构。
|
||||||
|
- **参数错误**:缺必填字段时返回**结构化** error(字段/码),而非未捕获裸异常。
|
||||||
|
- **外部异常**:adapter 超时、鉴权失败、脏响应等被转换为**可读、可重试标识明确**的错误形态(按产品约定)。
|
||||||
|
|
||||||
|
5. **给 AI 的提示**
|
||||||
|
复制后全文搜索 `your_service`、`TODO`、`伪代码` 等占位符并替换;不要留下指向真实客户的示例数据。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 如何启用 golden case 样例
|
||||||
|
|
||||||
|
1. **复制文件**
|
||||||
|
`tests/samples/test_golden_cases.py.sample`
|
||||||
|
→ `tests/test_golden_cases.py`(或 `tests/test_<domain>_golden.py`)。
|
||||||
|
|
||||||
|
2. **按业务修改 fixtures**(仍须脱敏)
|
||||||
|
- `tests/fixtures/sample_request.json`:请求或输入快照。
|
||||||
|
- `tests/fixtures/expected_response.json`:期望摘要或关键字段。
|
||||||
|
可新增多组文件,如 `case_valid_001.json`,并在测试中显式读取路径。
|
||||||
|
|
||||||
|
3. **断言建议(避免脆弱用例)**
|
||||||
|
- 比较**关键字段**,避免对整段 JSON 做机械全文 diff。
|
||||||
|
- **忽略**时间戳、随机 id、`trace_id` 等非确定字段。
|
||||||
|
- 对列表:断言**长度**、**关键元素**、**排序规则**(若业务保证顺序)。
|
||||||
|
- 对金额/数量:断言 Decimal 字符串、或允许文档约定的小范围误差。
|
||||||
|
|
||||||
|
4. **与 service 的关系**
|
||||||
|
golden 用例仍应通过 **service 层**(或纯函数)得到输出,再与 fixture 对比,而不是从 CLI 字符串 scrape。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 不应该写在 samples 复制件里的内容
|
||||||
|
|
||||||
|
以下若出现,应移到 **`tests/integration/*.sample`** 或 **`tests/desktop/*.sample`**,并加环境变量授权说明:
|
||||||
|
|
||||||
|
- 访问**真实** API 或内网服务。
|
||||||
|
- 打开**真实** RPA 页面或生产门户。
|
||||||
|
- 读取**真实**客户文件、生产数据库。
|
||||||
|
- 在测试中写入**真实**数据根或共享盘。
|
||||||
|
- 任何真实 **token / cookie / 密码** 字面量。
|
||||||
|
- 耗时极长、强依赖宿主 UI 抖动、无法在 CI 稳定复现的用例(不要塞进默认 `tests/test_*.py`)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 推荐命名(复制到根目录后)
|
||||||
|
|
||||||
|
| 用途 | 示例文件名 |
|
||||||
|
|------|------------|
|
||||||
|
| Service 契约 | `tests/test_service_contract.py` |
|
||||||
|
| Golden / fixture 回归 | `tests/test_golden_cases.py` |
|
||||||
|
| 领域规则 | `tests/test_<domain>_rules.py` |
|
||||||
|
| 校验逻辑 | `tests/test_<domain>_validation.py` |
|
||||||
|
|
||||||
|
命名需满足:`tests/` 根目录下 `test_*.py`,以便 `python tests/run_tests.py` 默认发现。
|
||||||
|
|
||||||
|
更多分层说明见 **`tests/README.md`**(「我该把测试写在哪里?」与「新技能最小测试清单」)。
|
||||||
|
|||||||
Reference in New Issue
Block a user