• 简体中文
  • 编写和运行测试用例

    框架维护者完成 Test Project 配置后,人类或 Agent 可以使用项目注册的 Node 编写测试用例。开始编写前,建议运行 describe-nodes 生成 Node 说明书,确认项目提供的能力和输入参数。

    如果你尚不了解整体设计,请先阅读 Test Runner 概览。如果项目尚未配置,请阅读扩展和维护 Test Runner

    核心概念

    @midscene/test 使用以下概念描述测试项目。

    Test Project 配置
    └── Execution Project(Web / Android / iOS / computer)
        └── Workflow Document
            ├── Lifecycle Step
            └── Case
                └── Step
                    └── Node
    • Test Project(测试项目):由 midscene.config.ts 定义的顶层配置,负责注册共享 Node,并配置默认运行目标或 projects 数组。
    • Execution Project(执行项目):Test Project 内的一项子配置,描述一个运行目标及其平台、Project setup、文件和标签筛选、变量与 retry 策略;它不是独立于 Test Project 的同级实体。
    • Workflow Document(工作流文档):被 Execution Project 选中的 YAML 文件,包含生命周期和多个 Case。
    • Lifecycle Step(生命周期步骤):在 Workflow Document 的 beforeAllbeforeEachafterEachafterAll 钩子中声明并执行的步骤,用于进行数据准备、状态重置、资源清理等辅助性操作。
    • Case(用例):由多个 Step 组成的具名测试用例。
    • Step(步骤):在 YAML 中对 Node 的一次调用。
    • Node(节点):平台开发者注册的执行能力,例如 aiAssertorder.create

    Node 定义团队可用的执行能力,YAML 定义每个测试用例如何组合这些能力。

    编写 YAML 测试用例

    每个 Workflow Document 必须包含非空的 cases 数组。每个 Case 必须包含 name 和非空的 steps

    cases:
      - name: 创建订单
        steps:
          - order.create:
              sku: midscene-mug
              quantity: 1
          - aiAssert: 页面显示“下单成功”
    
      - name: 取消订单
        steps:
          - order.cancel:
              orderId: example-order-id
          - aiAssert: 页面显示“订单已取消”

    运行器按照 YAML 中的声明顺序执行 Case。一个 Case 失败后,运行器会记录失败结果,并继续执行后续 Case。

    编写 Step

    每个 Step 只能调用一个 Node。如果调用时只需传入 prompt 参数,可以使用字符串简写。

    steps:
      - aiAct: 点击提交订单按钮

    上面的写法等价于:

    steps:
      - aiAct:
          prompt: 点击提交订单按钮

    业务 Node 可以接收自定义参数。

    steps:
      - order.create:
          sku: midscene-mug
          quantity: 2

    设置超时和错误处理

    $ 用于设置由运行器控制的 Step 参数。运行器不会将这些参数传入 Node 的 input

    steps:
      - order.create:
          sku: midscene-mug
          $:
            timeout: 30000
            continue-on-error: true

    支持以下两个字段:

    • timeout:Step 的超时时间,单位为毫秒。
    • continue-on-error:设为 true 后,即使 Step 失败,运行器也会继续执行当前阶段的后续 Step。默认值为 false

    continue-on-error 只控制运行器是否继续执行。只要有 Step 失败,Case 的最终状态就是 failed

    使用 Project 变量与环境变量

    运行器会在执行前递归解析 Node input:

    steps:
      - launch:
          uri: ${appUri}
      - api.createOrder:
          baseURL: ${{TEST_API_BASE_URL}}
          payload:
            count: ${orderCount}
    • ${name} 读取当前 Execution Project 的 variables;独占整个标量时保留原始 JSON 类型。
    • ${{ENV_NAME}} 读取环境变量,结果始终是字符串。
    • 对象或数组变量可以作为完整值使用,但不能嵌入更长的字符串。
    • 未定义变量会在收集阶段失败。变量只解析 Node input,不解析 $

    Workflow YAML 不提供 setsaveAs 或 Step 输出表达式。自然语言 Node 可以通过运行器维护的只读执行历史理解前序结果。

    使用 tags 筛选 Case

    cases:
      - name: Android 冒烟下单
        tags: [smoke, android]
        steps:
          - aiAct: 完成下单

    框架维护者在每个 Execution Project 中配置 tags.includetags.exclude。exclude 始终优先;include 非空时,Case 命中任意一个 include tag 即会被选中。

    定义执行生命周期

    Workflow Document 可以在 cases 前后声明生命周期 Step。

    beforeAll:
      - data.prepare: 创建本文件需要的测试数据
    
    beforeEach:
      - browser.reset: 将页面恢复到初始状态
    
    cases:
      - name: 创建订单
        steps:
          - aiAct: 创建一个订单
          - aiAssert: 页面显示“下单成功”
    
      - name: 取消订单
        steps:
          - aiAct: 取消最新订单
          - aiAssert: 页面显示“订单已取消”
    
    afterEach:
      - report.save: 保存当前用例的执行信息
    
    afterAll:
      - data.cleanup: 删除本文件创建的测试数据

    一个 Execution Project 的完整执行顺序如下。

    Project setup
      Workflow Document 1
        beforeAll
          Case 1 attempt 1:beforeEach → steps → afterEach
          Case 1 retry:    beforeEach → steps → afterEach
          Case 2 attempt 1:beforeEach → steps → afterEach
        afterAll
        Node 文档级清理
      Workflow Document 2
        ...
    Project teardown

    各阶段的职责如下:

    • beforeAllafterAll 对每个 YAML 文件各运行一次,负责文档级业务准备与清理。
    • beforeEachafterEach 对每次 Case attempt 各运行一次。
    • retry 会以新的 run ID、Agent scope、缓存 scope 和 Case history 重跑整个 Case,但不会重跑 beforeAll
    • Node 可以注册 attempt 或 Document scope 的内部清理,例如释放 Agent 和生成报告;这些清理在相应 scope 结束时执行。

    即使 Case 主体失败,afterEach 仍会执行。

    如果 beforeAll 失败,运行器会将当前文件中的 Case 标记为 not-run,但仍会执行 afterAll 和已经注册的 Node 清理。

    Project setup 在该 Project 的 Workflow Document 之前只执行一次,Project teardown 始终按 LIFO 顺序执行。收到中断信号时,当前 Step 会被取消,但 cleanup hook 与已注册 teardown 会使用可执行清理工作的 signal。

    运行测试

    在项目根目录下,使用以下命令运行 YAML 测试用例:

    pnpm exec midscene-test

    指定用例目录或文件

    默认情况下,运行器会递归查找并执行项目根目录下的所有 .yaml.yml 文件(自动忽略 node_modules.git)。

    如果你只想执行特定目录或特定用例文件,可以将其作为参数传入:

    # 运行指定目录下的所有用例
    pnpm exec midscene-test ./cases/smoke
    
    # 运行单个指定的用例文件
    pnpm exec midscene-test ./cases/order.yaml

    过滤运行目标与配置文件

    如果项目配置了多个运行平台或环境,你可以指定仅运行特定的 Execution Project,或者通过命令行指定自定义配置文件:

    # 只运行名为 android-smoke 和 ios-regression 的 Execution Project
    pnpm exec midscene-test --project android-smoke --project ios-regression
    
    # 使用指定的配置文件运行测试
    pnpm exec midscene-test --config ./config/midscene.config.ts

    当发生用例运行失败、文档解析失败或收集阶段发生错误时,CLI 会返回退出码 1

    查看测试结果

    每次测试运行结束后,你可以在控制台直接看到用例的执行状态和简要汇总。同时,运行器会在本地生成可视化的测试报告。

    Midscene 可视化报告

    运行器会自动将详细的测试步骤、界面截图和 AI 决策过程记录到交互式 HTML 报告中。

    默认情况下,报告保存在:

    midscene_run/report/

    你只需在浏览器中打开该目录下的 HTML 文件,即可直观地看到每一个 aiActaiAssert 的执行轨迹、元素定位结果以及完整的历史截图。

    提示:如果想修改可视化报告的默认保存目录,可以通过修改 midscene.config.ts 中的 output.reportDir 配置来实现。

    当前限制

    目前版本的测试运行器存在以下局限性,在编写用例时需予以注意:

    • 无并行执行:在单个 Execution Project 内部,所有的 Workflow Document、Case、attempt 和 Step 均为串行执行,暂不支持用例维度的并发。
    • 无控制流:不支持 DAG(有向无环图)、分支(If-Else)或循环(Loop)等控制流,用例按照声明顺序线性执行。
    • 无用例间数据依赖:不同的 Workflow Document 之间是完全隔离的,无法传递或共享执行中的数据。
    • 仅限本地加载:暂不支持直接从远程 URL、npm 包或 Git 仓库加载并执行 YAML 文件。

    此外,运行器会在执行前对所有 YAML 文件进行静态收集和校验。若存在未知的顶层字段、未注册的 Node 或无效的 Step,运行器会立即抛出收集错误(Collection Error)并中断运行,不会等到执行该 Case 时才报错。