Skip to content

用例写法 ​

用例是 YAML 文件,放在 cases/web/(浏览器)或 cases/android/(手机)。一个文件对应一个业务场景,里面可以有多个 case。

文件结构 ​

yaml
# cases/web/cart.yaml
beforeAll:                 # 整个文件开始前跑一次
  - setViewportSize:
      width: 1280
      height: 720

beforeEach:                # 每个 case 开始前跑
  - gotoUrl: https://<测试环境域名>/
  - aiAct: 如果有弹窗或广告,关掉它

cases:
  - name: 加购后购物车数量正确
    tags: [smoke, cart]
    steps:
      - aiAct: 在搜索框输入"蓝牙耳机"并回车
      - aiAct: 点击第一个搜索结果
      - aiAct: 点击"加入购物车"
      - aiAssert: 购物车角标显示 1

  - name: 修改购物车数量
    tags: [cart]
    steps:
      - aiAct: 打开购物车
      - aiAct: 把第一个商品的数量改为 2
      - aiAssert: 该商品小计等于单价的两倍

afterEach:                 # 每个 case 结束后跑,失败也跑
  - recordToReport:
      title: 购物车状态

生命周期钩子:beforeAll / beforeEach / afterEach / afterAll,都可选。

步骤怎么写 ​

操作:aiAct ​

一句话说清在哪里、做什么。

yaml
- aiAct: 点击顶部导航的"我的订单"
- aiAct: 在收货地址表单里,姓名填"测试账号",手机号填 13800000000
- aiAct: 向下滚动直到看到"评价"区域

写法建议:

  • 一步只做一件事,别把三个动作塞一行。
  • 用页面上看得见的文字描述目标,比如按钮上的字、标签名。
  • 有歧义时加位置限定:"右上角的"、"商品卡片里的"、"弹窗底部的"。
  • 不要写实现细节(class、id、坐标),模型看的是截图。

精确操作:aiTap / aiInput / aiScroll ​

当 aiAct 不稳定时,用拆开的指令,每个只做一个动作:

yaml
- aiTap: 搜索按钮
- aiInput:
    prompt: 顶部搜索框          # 在哪里输入
    value: 蓝牙耳机             # 输入什么
- aiKeyboardPress: Enter
- aiScroll:
    prompt: 商品列表区域        # 可省略,默认滚整个页面
    options:
      direction: down
      scrollType: untilBottom   # once / untilTop / untilBottom / untilLeft / untilRight

断言:aiAssert ​

描述页面应该是什么样,失败时 message 会出现在报告里。

yaml
- aiAssert:
    prompt: 订单详情页显示状态为"待发货",实付金额为 ¥199.00
    message: 下单后订单状态或金额不对

断言要具体、可判断:写"显示 3 件商品"而不是"商品数量正确"。

等待:aiWaitFor / sleep ​

yaml
- aiWaitFor: 支付成功页面出现"支付成功"字样
- sleep:
    ms: 1500

页面有动画、跳转、接口慢时先等再断言。aiWaitFor 会轮询直到条件满足或超时。

取数据:aiQuery ​

把页面上的信息抽成结构化数据,结果记在这一步的结果和报告里,方便人工核对。

yaml
- aiQuery: 购物车里每个商品的名称、单价、数量,以数组返回

注意:步骤结果不会自动传给后面的步骤。要用取到的值做判断,直接写成 aiAssert 的描述更简单。

超时 ​

每个 case 的总超时在 midscene.config.ts 的 test.testTimeout 里统一设(当前 10 分钟)。个别慢步骤可以单独设步骤超时,单位毫秒:

yaml
- aiAct:
    prompt: 点击"提交订单"
    $:
      timeout: 180000

Web 专用节点 ​

节点用途
gotoUrl打开页面
setViewportSize设窗口大小,建议固定 1280×720 或模拟手机 390×844
setCookies / clearCookies免登录:从环境变量或 storage state 文件灌 cookie
javascript在页面里执行 JS,比如清 localStorage

Android 专用节点 ​

节点用途
launch拉起 App,参数是包名,如 com.example.shop
terminate杀进程
back / home系统返回键、Home 键
runAdbShell执行 adb shell 命令,比如清缓存

完整列表和参数见节点参考。

自定义确定性节点 ​

需要查数据库、调后台接口、重置测试数据时,在 nodes/ 下用 TypeScript 写节点,再挂到 midscene.config.ts 对应 project 的 nodes 数组里。参考 nodes/todo.ts:

ts
import { defineNode, z } from '@midscene/test';

export const resetCart = defineNode({
  name: 'shop.resetCart',
  title: '清空测试账号购物车',
  inputSchema: z.strictObject({ userId: z.string() }),
  async execute({ input }) {
    await fetch(`https://<测试环境>/internal/cart/${input.userId}`, { method: 'DELETE' });
    return { summary: `已清空 ${input.userId} 的购物车` };
  },
});

挂上以后 YAML 里直接用:

yaml
beforeEach:
  - shop.resetCart:
      userId: test-001

改完 nodes/ 跑一次 pnpm nodes 刷新节点清单。

给模型加背景知识 ​

页面有固定套路(比如每次进来都弹优惠券)时,用 setAIContext 一次性告诉模型,后面所有步骤都生效:

yaml
beforeEach:
  - setAIContext:
      target: default            # default 对所有 AI 节点生效,也可以只给 aiAssert 等某一类
      context: |
        这是一个电商网站。遇到优惠券弹窗、新人引导时先关闭再继续。
        价格都以人民币显示,¥ 符号后面是金额。

不传 context 则清除之前设置的背景。

命名和目录约定 ​

cases/
  web/
    search.yaml        搜索与筛选
    cart.yaml          购物车
    checkout.yaml      下单与支付
    account.yaml       登录注册、个人中心
    after-sale.yaml    退换货
  android/
    00-smoke.yaml      不调模型的冒烟,验证设备连接
    ...                与 web 同名
  • 文件名小写、用短横线,按业务模块分。
  • case 的 name 用中文写清楚场景和预期。
  • 每个 case 至少一个 aiAssert,没有断言的用例等于没测。

内部文档,仅供测试团队使用