AI测试 夯爆了!分享几个我在用的,基于业务场景接口串联测试 Skill !

狂师 · 2026年10月27日 · 105 次阅读

在 AI 赋能自动化测试全流程,前面分享的系列文章中,我们把接口自动化测试的智能化流水线完整跑通了。

  • 007 篇批量生成了 59 个接口、2000 多条测试数据、159 个测试脚本。
  • 009 篇让这套脚本实现了智能执行、失败自愈与自动报告,P0 级用例通过率最终稳定在 98% 以上。

但如果你停下来,仔细看一眼这套用例库,会发现一个问题:所有用例,测的都是"单接口"。

  • test_login_success 验证的是登录接口本身,
  • test_add_to_cart_boundary 验证的是加购接口的参数边界,
  • test_create_order_normal 验证的是下单接口的正常返回。

它们彼此之间,没有任何关系——登录用例自己造账号,下单用例自己造订单号,谁也不认识谁。

这带来一个非常典型、也非常危险的场景:

登录接口返回 200,加购接口返回 200,下单接口也返回 200——单接口用例全绿。但用户在页面上发现,下单成功后订单状态一直卡在"待支付",库存也有扣减。你回头去查单接口用例,每一条都是通过的。

问题出在哪?出在接口与接口的衔接处:token 没有正确传递、订单状态机流转卡死、库存扣减与订单创建的数据不一致。这类缺陷,任何一条单接口用例都拦不住。

单接口测试回答的是"这个接口自身对不对",而用户真正关心的是"这条业务流程通不通"。用户用你的产品,从来不只是为了单纯"调用接口",用户真正用的是业务流程。

本篇教程,我们就来补上这块拼图——基于业务场景的接口串联编排测试:把"注册 → 登录 → 搜索 → 加购 → 下单 → 支付 → 查单"这样一条真实业务链路,作为一个完整的测试对象,让前面篇章里那些孤立的单接口,按照业务逻辑串起来跑。

基于业务场景的接口串联编排测试

单接口测试能解决了什么,不能解决什么?

先客观地回顾一下,前面建立起来的利用 AI 赋能单接口测试体系,到底解决了什么。

站在单个接口的视角,这套体系已经做得相当完整:

  • api-schema-parser 把 接口的定义结构化
  • api-testdata-generator 为每个接口构造了正向、边界、异常、安全四个维度的测试数据
  • api-testscript-generator 按"接口层 / 用例层 / 数据层"分层规范批量生成了脚本,
  • api-test-optimizer 又从 10 个维度做了场景补齐。参数校验、必填项校验、边界极值、非法格式、SQL 注入、无鉴权访问……这些"接口自身作为独立服务单元"的质量问题,都被覆盖到了。

这对应的是测试分层里经典的"点"——每个接口作为一个独立的契约,验证它的输入输出是否符合定义。

但接口自动化测试的完整版图,是"点、线、面"三层:

层次 测试对象 回答的问题 前面篇章覆盖情况
点(单接口) 一个接口 这个接口自身的契约对不对? ✅ 已完整覆盖
线(业务链路) 一串有依赖关系的接口 这条业务流程跑不跑得通? ⚠️ 仅浅覆盖
面(跨模块一致性) 多条链路共享的数据与状态 数据和状态在系统里是否一致? ❌ 基本空白

"线"和"面"的空白,具体表现为四类单接口测试天然测不到的缺陷:

  1. 数据传递缺陷:上游接口的产出(token、商品 ID、订单号、购物车项 ID)没有被下游接口正确消费。单接口测试里这些值都是各自 mock 或围绕接口自身构造的,传递链路从未被真实走过。
  2. 状态流转缺陷:订单状态机(待支付 → 已支付 → 已发货 → 已完成)、库存锁定与扣减、优惠券核销——状态在多个接口之间流转,,即便单接口全部正常,但任何一环卡死,用户使用时还是会报障。
  3. 数据一致性缺陷:下单后,订单表、库存表、购物车表三处数据要同步变化。单接口断言只看当前接口的响应,看不见别处的数据。
  4. 业务规则联动缺陷:比如"库存不足时禁止下单"“未支付订单 30 分钟自动取消",规则本身横跨多个接口和后台任务。

为什么说业务场景测试才是接口自动化测试的重点

这个问题,可以从三个角度来看。

第一个角度,是缺陷的价值分布。

单接口测试拦住的,多是参数校验类的低层级缺陷——这类缺陷开发自测阶段大多能发现,漏到测试环节,修复成本也低。而真正会造成资损、客诉、线上事故的,是流程类缺陷:订单状态卡死、重复支付、库存超卖、优惠叠加计算错误。

这类缺陷藏在接口的衔接处和状态的流转处,只有把链路串起来跑,才暴露得出来。缺陷越靠近业务流程,单接口测试越无能为力,而它的危害恰恰越大。

第二个角度,是回归测试的效率。

版本迭代时,测试团队最先要保住的是什么?是核心业务链路。

一条"登录 → 加购 → 下单 → 支付"的场景用例,一次执行就完成了十几个接口的业务级回归,直接回答了"这个版本核心流程还能不能用"。但相比之下,跑 200 条彼此独立的单接口用例,也无法给出这个结论——因为它们加起来不构成一条流程。

这就是为什么很多团队的接口自动化跑了很多年,开发和产品还是会在发版前问一句"主流程过了没"——单接口通过率回答不了这个问题,业务场景测试才能。

第三个角度,是企业落地的现状与认知。

一个略显扎心的现实是:大量团队的接口自动化,就止步于单接口。用例数量很可观,报告很好看,但业务价值感知度低,开发不信任、老板不买账,久而久之自动化就成了"为了跑而跑"。

反过来,无论是在企业内部推动自动化建设,还是在求职面试中,"接口自动化怎么覆盖业务闭环""跨接口数据依赖怎么处理"都是最高频的问题之一。

能不能把业务场景串联测试做起来,很大程度上决定了一套接口自动化体系是"数量可观"还是"价值可信"。

打个比方:单接口测试是自动化体系的"砖",每一块都方正合格;业务场景测试才是"楼"——用户住的是楼,不是砖。没有砖盖不了楼,但只验砖、不验楼,交付的就是一堆合格的砖。

实战项目准备

本篇教程的实战案例,依旧延续 shop-lab 全栈电商项目。如果你尚未在本地搭建该项目,请参考 007 篇 1.6 章节 完成环境初始化。

本篇的业务场景链路,会横跨项目的 5 个业务模块:

模块 链路中承担的角色
auth(认证) 注册、登录,产出全程要用的 token
search(搜索) 找到目标商品,产出 productId
product(商品) 查看详情,获取库存基线 stockBefore
cart(购物车) 加购,产出 cartItemId
order(订单) 下单、支付、查单,是断言的主战场

请确保后端服务已正常启动,Swagger UI 可访问:http://localhost:8080/swagger-ui.html,

并且 007 篇生成的 api_definitions.json 接口定义文件还在(本篇场景建模的核心输入之一;如果丢了,用 api-schema-parser 重新解析一遍 /v3/api-docs 即可)。

业务场景接口编排测试 Skill

为了实现基于业务场景接口编排测试,我们新开发了两个 Skill。

🔹 api-scenario-modeler(场景建模)
🔹 api-scenario-runner(链路执行引擎)

两个 Skill 一个管「测什么」,一个管「怎么跑」,和前面篇章的执行、诊断、清理、报告技能无缝衔接,单接口和业务场景两条线从此在同一个体系里跑。

这篇先来拆解一下 api-scenario-modeler(场景建模)第一个 Skill。

api-scenario-modeler 介绍

把自然语言描述的业务场景翻译成标准化的 scenario_<场景名>.yaml 场景编排文件 + modeling_notes.md 建模说明,作为下游执行引擎 api-scenario-runner 的直接输入。

核心定位是"场景执行链路的翻译环节",只做一件事:把业务场景翻译成编排文件。

适用场景

  • 把业务流程翻译成接口用例(注册→登录→搜索→加购→下单→支付类端到端链路)
  • 生成场景 yaml、做场景建模、编排一条接口链路
  • 提供了业务场景描述 + api_definitions.json,需要产出可执行的编排文件

使用方式

在 Claude Code 中提供业务场景描述与 api_definitions.json,或输入:

/api-scenario-modeler

建模工作流(8 步)

核心推导规则(R1~R7)

规则 说明
R1 鉴权前置 登录类步骤在前,token 由登录响应提取,后续步骤由 runner 统一注入
R2 数据依赖定向 参数依赖谁产出,就排在谁后面(排序唯一依据)
R3 禁止硬编码动态值 ID/token/单号一律 extract 注入,不写死
R4 幂等策略 唯一性字段(用户名/手机号/邮箱)用 timestamp/uuid 动态生成
R5 断言分级 每步状态码 + 业务码;关键业务步骤追加字段断言;禁断动态字段全等
R6 终态校验 数据变更链路必须设计 db_asserts 终态一致性断言
R7 失败策略 happy path 用 abort,异常分支验证用 continue 并断言错误码

质量红线(脚本校验 + 人工复核)

  • 接口真实性:编排中每个 api 均存在于 api_definitions.json,绝不虚构接口,缺失环节标记"接口缺失"
  • 变量闭环:每个 ${} 引用的变量必有来源,无孤儿变量
  • 顺序合法、断言覆盖、幂等保障、无动态字段全等断言

实操演示

1、输入一段自然语言场景描述 + 接口定义文件:

请调用 api-scenario-modeler 技能,基于接口定义文件 /Users/xxx/output/api_definitions.json,
为下述业务场景生成串联编排文件:

新用户注册并登录后,搜索一件商品,查看商品详情,加入购物车,创建订单并完成支付,
最后确认订单状态已流转、库存扣减数量正确。业务规则:已支付订单不可取消。

建模完成后,同时输出 modeling_notes.md。

拿到编排文件和建模说明后,逐项过这张人工评审清单(modeler 的自检清单是 AI 自查,这一步是人查,两道关都要过):

评审项 检查方法 合格标准
接口真实性 对照 api_definitions.json 逐个核对 api 字段 零虚构
步骤顺序 重点看注册→登录、加购→下单、下单→支付的先后 数据依赖全部先产出后使用
变量闭环 对照变量流转图,逐个 ${var} 找来源 无孤儿变量、无硬编码动态值
断言分级 看下单、支付步骤是否有业务字段断言(订单状态流转) 关键步骤三级断言齐全
幂等设计 看 username 是否用了 timestamp 策略 唯一性字段全部动态化
存疑点 看 modeling_notes.md 是否标记了 addressId 这类前置数据 存疑点如实暴露,而非沉默假设

scenario_shopping_flow.yaml 业务场景排编文件详细示例(也称之为业务场景测试建模文件)

scenario:
  id: SC-order-001
  name: 新用户购物主链路(下单支付与库存终态校验)
  description: 新用户注册登录后搜索商品、查看详情、加购、下单并支付,校验订单状态流转为已支付、库存扣减恰好等于下单数量,且已支付订单不可取消
  priority: P0
  tags: [smoke, module:order, scene:business-flow]

variables:
  - { name: username, strategy: timestamp, prefix: shop_ }
  - { name: phone, strategy: random, prefix: "138" }
  - { name: password, value: Test@123456 }

steps:
  - step: 1
    name: 注册新用户
    api: POST /api/auth/register
    auth: false
    request:
      body:
        username: ${username}
        password: ${password}
        email: ${username}@qq.com
        phone: ${phone}
    extract: []
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
    on_failure: abort

  - step: 2
    name: 用户登录获取令牌
    api: POST /api/auth/login
    auth: false
    request:
      body:
        username: ${username}
        password: ${password}
    extract:
      - { var: token, from: $.data.token }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.token, op: not_null }
    on_failure: abort

  - step: 3
    name: 搜索目标商品
    api: GET /api/search
    auth: false
    request:
      query:
        keyword: 手机
        page: 1
        size: 10
    extract:
      - { var: productId, from: "$.data.records[0].id" }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.records, op: not_empty }
    on_failure: abort

  - step: 4
    name: 查看商品详情获取库存基线
    api: GET /api/product/detail/{id}
    auth: false
    request:
      path: { id: "${productId}" }
    extract:
      - { var: stockBefore, from: $.data.stock }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.stock, op: not_null }
    on_failure: abort

  - step: 5
    name: 新增收货地址
    api: POST /api/address
    auth: true
    request:
      body:
        name: 自动化测试收件人
        phone: ${phone}
        province: 上海市
        city: 上海市
        district: 浦东新区
        detailAddress: 张江高科纳贤路800号
    extract:
      - { var: addressId, from: $.data.id }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.id, op: not_null }
    on_failure: abort

  - step: 6
    name: 加入购物车
    api: POST /api/cart
    auth: true
    request:
      body:
        productId: ${productId}
        quantity: 1
    extract:
      - { var: cartItemId, from: $.data.id }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.id, op: not_null }
    on_failure: abort

  - step: 7
    name: 创建订单
    api: POST /api/order
    auth: true
    request:
      body:
        addressId: ${addressId}
        paymentType: 1
        cartIds: ["${cartItemId}"]
    extract:
      - { var: orderId, from: $.data.id }
      - { var: orderNo, from: $.data.orderNo }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.orderNo, op: not_null }
      - { type: jsonpath, path: $.data.status, op: equals, value: 10 }
    on_failure: abort

  - step: 8
    name: 支付订单
    api: POST /api/order/no/{orderNo}/pay
    auth: true
    request:
      path: { orderNo: "${orderNo}" }
      body:
        payMethod: 1
    extract: []
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
    on_failure: abort

  - step: 9
    name: 查询订单确认状态已流转
    api: GET /api/order/{id}
    auth: true
    request:
      path: { id: "${orderId}" }
    extract:
      - { var: orderStatus, from: $.data.status }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.status, op: equals, value: 20 }
      - { type: jsonpath, path: $.data.orderNo, op: equals, value: "${orderNo}" }
    on_failure: abort

  - step: 10
    name: 取消已支付订单(预期被拒绝)
    api: POST /api/order/{id}/cancel
    auth: true
    request:
      path: { id: "${orderId}" }
    extract: []
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: not_equals, value: 0 }
    on_failure: continue

  - step: 11
    name: 复查订单状态未被取消影响
    api: GET /api/order/{id}
    auth: true
    request:
      path: { id: "${orderId}" }
    extract: []
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.status, op: equals, value: 20 }
    on_failure: abort

  - step: 12
    name: 复查商品详情获取库存终态
    api: GET /api/product/detail/{id}
    auth: false
    request:
      path: { id: "${productId}" }
    extract:
      - { var: stockAfter, from: $.data.stock }
    assert:
      - { type: status_code, value: 200 }
      - { type: jsonpath, path: $.code, op: equals, value: 0 }
      - { type: jsonpath, path: $.data.stock, op: not_null }
    on_failure: abort

postconditions:
  db_asserts:
    - { name: 订单终态为已支付, sql: "SELECT status FROM t_order WHERE order_no = '${orderNo}'", expect_value: 20 }
    - { name: 库存扣减与下单数量一致, sql: "SELECT stock FROM t_product WHERE id = ${productId}", expect_expr: "${stockBefore} - 1" }
    - { name: API库存与DB库存一致, sql: "SELECT stock FROM t_product WHERE id = ${productId}", expect_expr: "${stockAfter}" }
    - { name: API订单状态与DB一致, sql: "SELECT status FROM t_order WHERE order_no = '${orderNo}'", expect_expr: "${orderStatus}" }
  cleanup:
    - { name: 回收测试用户与订单数据, via: api-testdata-cleaner, scope: ["user:${username}", "order:${orderNo}"] }

并且还会生成一份建模说明文件:modeling_notes.md,每个步骤对应的选择接口,以及选择理由。

各接口之间变量流转图(从变量名->来源定义->注入点)

还会基于业务场景自动生成一份存疑点点清单,这里的存疑点,意味着 AI 不太明确或者没有把握的地方,这个部分人工需要重点确认。

自检结果:

如何获取 Skill

所有实战演练创建生成的 skill,会统一上传到 GitHub 中,学员可按需自取。

同时,目前所有 Skill 均已统一上传至网站 testfather.cn Skill 技能商店中。

本篇新增的两个 Skill,安装方式与之前一致,将 Skill 目录复制或克隆到你的 Claude Code 或 WorkBuddy 技能目录下即可使用:


## 在线安装
npx skills add xxx/api-scenario-modeler
npx skills add xxx/api-scenario-runner

# 克隆仓库
git clone git@github.com:xxx/skills.git

# 复制到 Claude Code 技能目录
cp -r skills/api-scenario-modeler ~/.claude/skills/
cp -r skills/api-scenario-runner ~/.claude/skills/

# 或复制到 WorkBuddy 技能目录
cp -r skills/api-scenario-modeler /Users/xxx/.workbuddy/skills/
cp -r skills/api-scenario-runner /Users/xxx/.workbuddy/skills/
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册。