外观
用例写法
用例是 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: 180000Web 专用节点
| 节点 | 用途 |
|---|---|
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,没有断言的用例等于没测。