测试脚手架构建
为一个 target/module 建立唯一 Module Harness,或把新 suite 接入现有 Harness。
test-design -> Markdown suite
|
v
test-scaffold
|
v
test-codegen
Canonical 产物
test_workspace/targets/{target}/
target.yaml
helpers/ # 同一 target 内已有多个 module 复用时才存在
modules/{module}/
__init__.py
module.yaml
profile.md
fixture.py
harness.py
<responsibility>.py # 按需,如 api.py/resources.py
{suite_dir}/
suite.yaml
*.md
profile_{suite}_suite.md
固定运行契约:
setup_{module} -> {Module}Harness -> generated variable: harness
module.yaml:target/module/module_type、knowledge refs、registered suites。profile.md:模块级稳定assertion_rules和variables.defaults。fixture.py:只公开setup_{module},负责 pytest 生命周期和 Harness 装配。harness.py:模块测试能力门面和资源所有者。- suite profile:TC-ID 绑定的 variables、requests、structured assertions、case flows/bodies。
- 不建立
test_workspace/helpers/。单模块能力留在 module package;target helper 必须已有至少两个 module 真实复用。
必读参考
aitest_config/refs/config-files.md:配置字段和目录归属。refs/constraints.md:Harness contract、flow 边界和验证门禁。refs/formats.md:API Map、profile、Harness 和输出模板。
读写边界
默认只读 docs、knowledge、Markdown cases、公开 API/schema/路由声明、启动配置、现有测试资产和 aitest.yaml。文档不足时先列缺口,再请求用户确认是否读取更多声明层文件。
禁止:
- 修改待测系统、generated pytest、
.env或凭证文件。 - import 待测系统内部实现来伪造测试状态。
- 生成按
case_id分发请求、账号、配置或断言的 fixture/Harness。 - 为 suite 新建第二个公开 pytest fixture。
- 在 suite profile 配置 fixture/object/extra imports。
Step 0:选择模式
从 suite manifest、module registry 和目录推导 target/module;无法唯一确定才询问。
scaffold-module:新模块。必须有 L1/API 输入和一份最小冒烟 suite。scaffold-suite:Harness 已满足需求,只需补 suite manifest/profile。incremental:新用例暴露了 Harness 能力、env、setup 或 cleanup 缺口。
只是新增参数、断言组合或调用已有 capability 时,留在 test-codegen;需要新端点、认证、环境依赖、资源准备或 cleanup 时,进入 incremental。
Step 1:建立 API Map
分析公开接口和用例,输出 test_workspace/targets/{target}/api_maps/api_map_{module}.md:
- 端点、协议、认证和请求体。
- env 分层与 case variables/env 矩阵。
- 状态副作用、cleanup 和可行性判定。
- 信息缺口、manual/skipped 候选。
向用户分两段 review:先端点/认证,再 env/状态/可行性。格式见 refs/formats.md。
Step 2:设计 Harness 能力
先给出能力签名表,不直接写完整代码:
class GatewayHarness
login(username, password) -> httpx.Response
create_key(name) -> dict
delete_key(key_id) -> None
assert_key_active(payload) -> bool
close() -> None
每个 capability 标明:协议、认证、状态变更、env、cleanup。优先表达可复用业务动作,不写 run_case(case_id) 或每条用例一个方法。
复杂循环、条件、等待、临时文件和 mock 可以封装为 capability;fixture 仍只负责装配与生命周期。用户确认能力粒度后再实现。
Step 3:生成 module package
生成 canonical 五个文件,必要时再按职责拆文件:
module.yaml不配置 fixture/helper/profile 路径。harness.py定义{Module}Harness。fixture.py只公开setup_{module},直接 return/yield Harness。profile.md不放 TC-ID 绑定内容,也不重复module_type。- 使用
require_env()/require_envs();未调用的 capability 不应提前读取自己的可选 env。
先运行 Harness contract 和 compileall,再进入 profile。
Step 4:确认 profile 路线
选择 1-2 条代表 case 展示完整片段并让用户 review:
- 默认 HTTP 是否足够。
- 是否需要
requests.patches、structured assertions 或case_flow。 - flow 是否只调用
harness.*和前序变量。 - 复杂控制是否已封装为 capability,或确实需要
case_body。
case_flow 只支持 call、assign、assert、comment。profile 不选择 fixture/object,生成器从 module registry 绑定 setup_{module} 和 harness。
Step 5:生成 suite profile
- module profile 只放跨 suite 稳定规则/defaults。
- suite profile 放具体 TC-ID 的 variables、requests、structured assertions、case flows/bodies。
- pure manual 不写可执行 profile entry;半自动 manual 可写 flow/body 并保留 marker。
- 可行性存疑保持 skipped,不用 comment-only flow 冒充执行。
case_body只通过harness获取能力。
展示路线分布、manual/skipped 清单和保留 case_body 的原因。
Step 6:验证闭环
严格执行 refs/constraints.md#验证命令与预期:
aitest doctor--validate-profile--dump-ir/ 代表 case--explain- 正式 codegen
--check- compileall + suite collect
- 已注册 suite 的 module selector 检查
scaffold 完成表示测试资产能稳定进入 codegen,不代表真实服务断言已经通过。
跨模块 review
第二个模块起检查真实复用:
- 同一 target 已有两个 module 使用的纯协议/认证适配,可提取到 target helpers。
- 只属于当前 module 的业务动作、测试数据和断言继续留在 module package。
- 跨项目稳定能力才考虑进入
aitest_kit.helpers。 - 重复 flow/body 仅标记为
emitter-build候选,不自动晋升。
完成标准
- canonical module package 完整,
doctor的 Harness contract 无错误。 - 只有一个公开
setup_{module}fixture,并直接返回/yield Harness。 - fixture/Harness 不按 case_id 隐式分发;用例差异在 suite profile 显式表达。
- module/suite profile 归属正确,profile gate 无 ERROR。
- Case IR 的 structured flow 固定使用
setup_{module}和harness。 - codegen、freshness、compileall 和 collect 全部通过。
- module selector 能发现已注册 suite。
输出格式见 refs/formats.md#输出摘要模板。