skill-template 测试说明(企业数字员工技能)
本目录提供面向 ERP / TMS / WMS / 船司 / 报关 / 邮件 / 浏览器 / LLM / RPA 等外联场景的分层测试骨架:默认仅内存与本地 SQLite;真实 API / 真实 RPA 必须通过环境变量显式授权,且仅以 *.sample 或复制后的文件提供范式。
复制为新技能后,请保留隔离数据根与 profile 策略,再按域扩展用例。
我该把测试写在哪里?
人类开发者与 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能通过(含 platform-kit 导入与无 vendored core 守护)。python scripts/main.py health能通过(输出 runtime diagnostics;warning 不导致非零退出)。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(脱敏)。
- 如果技能有 Python 三方依赖,
requirements.txt已声明且版本约束合理(尽量收窄范围)。 health能在依赖缺失或原生运行库未就绪时给出清晰、可操作的错误(非裸 traceback)。- 真实 API / 真实 RPA 测试只放
tests/integration/,并且默认.sample,不默认运行。
1. 默认必跑(unittest)
在技能仓库根目录执行:
$env:PYTHONDONTWRITEBYTECODE = "1"
python tests/run_tests.py
python tests/run_tests.py -v
python tests/run_tests.py cli_smoke
python tests/run_tests.py test_cli_smoke
1.1 收集范围
run_tests.py只发现tests/根目录下test_*.py。- 不递归:
tests/samples/、tests/integration/、tests/desktop/(其中文件为*.sample或需 pytest 单独跑)。 - 启动时把
scripts/与tests/加入sys.path。 - Windows 下将 stdout/stderr 包装为 UTF-8。
1.2 默认套件覆盖
| 能力 | 文件 |
|---|---|
CLI 冒烟(import cli.app.main) |
test_cli_smoke.py |
subprocess 真实入口 python scripts/main.py |
test_entrypoint_subprocess.py |
| 运行路径与 CLAW_/JIANGCHANG_ 隔离 | test_runtime_paths.py |
SKILL.md slug 与 constants.SKILL_SLUG |
test_skill_metadata.py |
DB / task_logs 骨架冒烟 |
test_db_smoke.py |
| adapter profile / 外呼授权策略 | test_adapter_profile_policy.py + adapter_test_utils.py(非 test_ 前缀,不自动当用例收集) |
无 vendored jiangchang_skill_core |
test_platform_import.py |
| runtime diagnostics 架构守护 | test_runtime_diagnostics_integration.py |
| 文档/runtime 标准守护 | test_template_runtime_standard.py |
复制为新技能后不得删除上述架构守护测试,除非同步更新模板标准并理解影响。
1.3 数据隔离(_support.IsolatedDataRoot)
同时设置(进入同一临时目录 / 用户):
CLAW_DATA_ROOT |
JIANGCHANG_DATA_ROOT |
|---|---|
同一 tempfile.mkdtemp(prefix="skill-test-") |
同上 |
CLAW_USER_ID |
JIANGCHANG_USER_ID |
|---|---|
_test |
_test |
退出时四个键均恢复(原不存在则 pop),并删除临时目录。
1.4 产物与密钥
tests/.gitignore忽略__pycache__、.pytest_cache、*.pyc、desktop/artifacts/、integration/artifacts/、.env等。- 推荐运行前设置
PYTHONDONTWRITEBYTECODE=1减少.pyc。
2. 可选:Service / Golden(tests/samples/*.sample)
- 展示 service 契约 与 fixture 回归 写法;默认不执行。
- 启用:复制
tests/samples/test_service_contract.py.sample等为tests/test_*.py(去掉.sample),按业务修改后再python tests/run_tests.py。 - 脱敏样例数据见
tests/fixtures/README.md与sample_request.json/expected_response.json;profile 结构见adapter_profiles.sample.yaml。
3. 可选:Integration(tests/integration/*.sample)
- 默认不跑;文件均为
*.py.sample。 - 说明与开关见
tests/integration/README.md。 - 禁止在样例中硬编码真实 token、密码、生产 URL;凭证须来自密钥库或受控环境变量。
4. 可选:Desktop E2E(pytest)
见 tests/desktop/README.md。示例为 test_desktop_smoke.py.sample,需复制为 test_desktop_smoke.py 后用 pytest 运行。
5. 环境变量:测试档位与授权
5.1 测试档位(二选一变量名,后者为历史拼写兼容)
OPENCLAW_TEST_TARGET或OPENCLOW_TEST_TARGET
合法取值(非法值将使 get_test_target() 抛 ValueError):
| 取值 | 含义 |
|---|---|
unit |
默认:单元 / 内存 / mock,不跑仿真与真实外联 |
mock |
与 unit 类似的安全档位(显式语义) |
simulator_api |
仅允许 仿真 HTTP 类集成(如 localhost) |
simulator_rpa |
仅允许 仿真页面 / 录播 RPA |
real_api |
真实 API(另需 ALLOW_REAL_API=1) |
real_rpa |
真实 RPA(另需 ALLOW_REAL_RPA=1) |
未设置时等价 unit。
5.2 授权开关(显式 1)
| 变量 | 作用 |
|---|---|
ALLOW_REAL_API=1 |
允许 real_api profile 访问真实 HTTP 通道 |
ALLOW_REAL_RPA=1 |
允许 real_rpa profile 驱动真实浏览器/RPA |
ALLOW_WRITE_ACTIONS=1 |
在 real_* 下允许写操作(提交表单、下单等) |
5.3 默认安全策略(adapter_test_utils)
- 默认
unit:不访问真实 API、不跑真实 RPA、不做写操作、不读取真实密钥、不写真实数据根。 assert_no_real_network_env()断言默认未误开ALLOW_REAL_*(用于必跑用例自检)。
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 的关系
- 未复制 article / media / prompt / NotebookLM 等业务测试。
- 本骨架面向通用外联技能;业务契约请放在各自技能的
tests/samples/tests/integration复制件中实现。