npx skills add ...
npx skills add apifox.com/apifox-test-scenario
Apifox 测试场景建模:test-scenario 的查询、创建、更新、删除和运行;导入接口、单接口用例或其它场景步骤;添加场景引用步骤;复杂步骤编排、接口步骤/条件/循环/等待/脚本/数据库等步骤衔接、前后置操作、变量引用、断言、提取器和场景调试最佳实践。用户要创建或维护复杂自动化测试流程时使用。
npx skills add apifox.com/apifox-test-scenario
前置条件:先阅读
../apifox-cli/SKILL.md。若旧总入口与本 skill 的领域规则冲突,以当前 CLI help 和本 skill 为准。涉及单接口 case 时读取../apifox-test-case/SKILL.md,涉及执行/CI/报告边界时读取../apifox-test-automation/SKILL.md。环境和变量命令以当前 CLI help 为准。
具体命令参数以当前 CLI help 为准。创建和更新测试场景时重点处理步骤保存语义、导入/引用边界、变量传递、复杂步骤字段风险和运行验证边界。Agent 维护场景时优先使用导入/引用命令和 get --with-case-detail 回读真实结构,再做局部 update。
apifox-test-case。apifox-test-automation。test-report;执行和报告边界参考 apifox-test-automation。| 概念 | CLI 资源 | 说明 |
|---|---|---|
| 接口测试用例 | test-case | 绑定某个 endpoint 的 case,适合单接口验证 |
| 测试场景 | test-scenario | 多步骤流程编排,支持复杂步骤衔接 |
| 测试套件 | test-suite | 场景/用例集合与回归组织 |
| 环境 | environment | 提供 baseUrl、变量、服务配置 |
| 测试数据 | test-data | 场景或 case 的迭代数据来源 |
使用当前 CLI help 查询 test-scenario 的参数。import-steps 和 add-ref 未必出现在顶层“可用命令”列表,但各自支持 --help,可用于导入步骤和添加场景引用。
关键事实:test-scenario create 只保存元数据。即使 create payload 里包含 steps,步骤也不会在 create 阶段保存。正确流程是先 create 元数据,再 get --with-case-detail,再用 import-steps、add-ref 或 test-scenario update --file 添加或修改 steps。
常见导入优先使用高层命令,不要手写复杂 HTTP 绑定结构。已验证的入口包括:
语义边界:
import-steps 是复制/导入为当前场景自己的步骤。add-ref 是添加一个引用其它场景的步骤,不复制源场景内部步骤。--sync manual 是默认模式,适合导入后补业务参数、变量引用和断言。--sync auto 只适用于 endpoint/test-case 来源,表示随源接口定义或单接口用例自动同步;test-scenario 来源不支持 auto。简化创建必须给完整必填参数,但这只会创建空场景。除非用户明确要占位场景,否则“创建自动化测试/场景”不能停留在空场景。
test-scenario list/get 读取作为模板。test-scenario-create schema 创建元数据。test-scenario get --with-case-detail 回读完整结构。import-steps 或 add-ref,不要直接手写复杂绑定字段。test-scenario get --with-case-detail,确认步骤写入且 HTTP case/detail 展开正常。test-scenario update --file 补齐。test-scenario-update schema,基于完整结构添加或修改 steps。cli-schema validate 通过后 update,再 test-scenario get --with-case-detail 确认 steps 非空且结构正确。test-scenario-update schema 已包含步骤、前后置处理器、断言、提取变量和枚举值说明。首次编写 processor 时先看 schema,不要凭经验猜字段名、枚举值或旧格式。
设计每个步骤时都写清:
复杂流程建议分层:
{{$.1.response.body.token}}、{{$.2.response.body.data.id}};它只在自动化测试场景中生效,需要运行完整场景,单独运行某个步骤无法取值。{{token}} 等变量引用。pm.environment.set('runSuffix', suffix);。{{...}};使用 pm.variables.get("$.1.response.body.token")。{{$.步骤号...}} 是否仍指向正确步骤。body.data.id;先根据真实响应确认路径,例如 {{$.2.response.body.id}}、{{$.2.response.body.data.id}}、{{$.2.response.body.data[0].id}}。{{...}} 占位符,不要转义、拆分或改写,否则运行时无法替换。{{$.步骤号...}} 和 forEach 规则只适用于 test-scenario,不要写入单接口 test-case。列表响应可作为 forEach.parameters.array,循环内用当前元素引用后续字段:
循环子步骤中引用当前元素:{{$.2.element.id}}。如果默认取数组第一个元素,要显式写出索引,例如 {{$.7.response.body.data[0].id}};如果业务要求特定元素,先筛选,不要假设列表顺序稳定。
Apifox CLI 不会自动格式化场景内容,写入什么字符串,客户端就展示什么字符串。写入前先预格式化字符串字段:
requestBody.data。parameters.data。preProcessors[*].data、postProcessors[*].data。规则:
\n 写入。{ id, type, data, defaultEnable, enable },不要写旧式嵌套 { type, config }。id,尤其是 assertion、extractor、customScript;缺少 id 可能 validate 通过但运行器或客户端解析异常。data.variableType 使用 globals;如需指定生效范围,data.shareScope 优先使用 PROJECT。TEAM 是团队范围,可能依赖增值能力,除非用户明确要求团队范围,否则不要默认使用。cli-schema validate。import-steps 从 API definition 导入的步骤可能只有结构或 schema 示例值;运行前必须回读并按业务场景补齐 params、headers、body、脚本变量等。示例:
常规校验优先用可视化 assertion,自定义脚本只作为兜底能力。
断言规则:
assertion。assertion,不要默认写 customScript。httpCode,不要用 responseCode;JSON 字段用 responseJson,不要用 responseBody;全文包含用 responseText + include;比较符用 equal,不要用 equals。脚本规则:
pm 对象读写变量、访问响应和定义断言;脚本断言使用 pm.test(...) 包裹。pm.test 外裸调用 pm.response.json(),避免空响应或非 JSON 响应导致错误难定位。| 步骤类型 | 建模建议 |
|---|---|
| 接口请求步骤 | 先确认 endpoint/case 或直接请求结构,输出关键响应字段 |
| 条件分支 | 条件表达式必须基于已存在变量或响应字段 |
| 循环/迭代 | 明确最大次数、退出条件和失败策略 |
| 等待/轮询 | 明确等待上限,避免无限等待 |
| 脚本步骤 | 输入输出变量要显式,避免隐式全局副作用 |
| 数据库步骤 | 先确认连接、SQL 只操作测试数据,敏感信息不输出 |
| 清理步骤 | 即使主流程失败,也尽量能执行清理 |
number,不要只看回读数组顺序。type: "http" + bindId: <endpointId> + bindType: "API" + syncMode: "SYNC_WITH_API" + httpApiCase.apiDetailId 导入接口;后端会生成 relatedId。test-scenario import-steps;引用其它场景优先用 test-scenario add-ref。syncMode: "MANUAL" 承载定制参数、变量引用和断言,仅绑定 API 不等于可用业务场景。group/if/else/loop/forEach/onError 必须包含 disable=false、parameters、isOpen=true、children=[]。children,不要平铺。group 展示名写在 parameters.name,不是顶层 name。if 和条件 break 使用 parameters.keyVariable + operator + valueVariable,不要写 expression。else 和 onError 的 parameters 应为空对象 {}。loop 使用 parameters.count,不是 times 或其他字段。delay 使用 parameters.timeout,单位毫秒,不是 duration。script 步骤使用 parameters.type="customScript"、parameters.data=<JS code>、enable=true,不要写 language/code。customHttp 的 URL 字段叫 customHttpRequest.path,不是 url,且需要完整请求字段。testCaseRef 字段是 relatedId,不是 caseId。relatedId 是后端为场景步骤生成的 HTTP case ID,新建步骤时不要从旧场景复制复用。test-scenario get --with-case-detail 用于确认步骤树和 HTTP case/detail 内部配置,例如 Body、Header、postProcessors;如果用户明确要求验证可运行性,再执行运行验证并检查报告。onError 目前存在 CLI/schema/get 成功但客户端展开和配置展示不稳定的风险;涉及 onError 时不能仅凭 validate/get 成功判断可用,需要客户端确认。cli-schema validate 只保证基础 JSON 结构,不保证 runner、客户端或处理器一定能正确解析。test-scenario get --with-case-detail 确认步骤树非空且结构正确,但不要把 get 成功当成可运行。test-scenario run 并检查报告。test-case 的结构直接当作 test-scenario 步骤结构;导入单接口用例到场景优先使用 test-scenario import-steps --source test-case --endpoint <endpointId> --ids <caseIds>。import-steps、add-ref 或 update --file 写入 steps。get --with-case-detail 原结构,避免覆盖整个步骤树。| 现象 | 处理 |
|---|---|
| 场景创建成功但前端步骤不展示 | test-scenario get 看真实保存结构,必要时转 apifox-cli-checkup |
| 后续步骤变量为空 | 检查上游 extractor、响应路径、变量名和执行顺序 |
| 场景 run 失败但单接口成功 | 检查步骤间变量传递、环境、前置脚本和依赖顺序 |
| 循环或等待卡住 | 检查退出条件、最大次数、timeout |
| 清理没执行 | 检查失败策略和后置步骤配置 |
| 报告没有步骤详情 | 先按 apifox-test-automation 区分本地/云端报告,再必要时转 apifox-cli-checkup |
stepName: 这一步做什么
stepType: 使用哪类步骤
input: 请求、脚本、SQL、等待条件或引用变量
output: 提取哪些变量
dependsOn: 依赖哪些上游步骤输出
assertions: 成功条件
onError: 失败后继续、停止或清理
cleanup: 是否需要后置清理准备数据
鉴权/登录
主流程操作
结果查询与断言
副作用校验
清理资源{
"type": "forEach",
"parameters": {
"array": "{{$.1.response.body}}",
"disableOnError": false
}
}{
"requestBody": {
"type": "application/json",
"data": "{\n \"name\": \"Demo\",\n \"description\": \"Readable in client\"\n}"
},
"postProcessors": [
{
"id": "postProcessors.0.customScript",
"type": "customScript",
"data": "pm.test('返回 ID', function () {\n var body = pm.response.json();\n pm.expect(body.data.id).to.exist;\n});",
"defaultEnable": true,
"enable": true
},
{
"id": "postProcessors.1.extractor",
"type": "extractor",
"data": {
"variableName": "project_pet_name",
"variableType": "globals",
"shareScope": "PROJECT",
"subject": "responseJson",
"expression": "$.name"
},
"defaultEnable": true,
"enable": true
}
]
}{
"type": "assertion",
"data": {
"name": "返回 ID",
"subject": "responseJson",
"comparison": "exists",
"path": "$.data.id",
"value": ""
},
"defaultEnable": true,
"enable": true
}pm.environment.set("variable_key", "variable_value");
pm.variables.set("variable_key", "variable_value");
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("JSON value equals expected", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});