• 简体中文
  • 接入 Vitest

    编写 Android、iOS、HarmonyOS 和桌面端的 JavaScript / TypeScript 测试时,推荐使用 Vitest。Vitest 负责组织用例、执行生命周期钩子并汇总测试结果;Midscene Agent 负责连接设备、操作界面并执行断言。

    使用前准备

    • 运行环境:已安装 Node.js(版本要求 ^20.19.0 || ^22.12.0 || >=24.0.0)和 pnpm。可使用已有项目;新项目请先执行 pnpm init(已有 package.json 可跳过)。
    • 模型配置与验证:已完成模型配置并通过模型验证,且已将配置保存在项目的 .env 文件或当前 Shell 环境中。
    • 平台环境:已完成目标平台的环境准备:Android、iOS、HarmonyOS 或桌面端,设备连接与权限已就绪。
    • (可选)试用 Playground:建议先在对应平台的 Playground中对目标界面试跑一次操作和断言,确认截图、交互与模型表现正常。
    选择接入平台

    1. 安装依赖

    pnpm add -D vitest dotenv @midscene/android

    2. 配置测试

    新建 vitest.config.ts。如果项目已在使用 Vitest,可以将设备测试配置单独保存为 vitest.device.config.ts,运行时通过 --config vitest.device.config.ts 指定,以免影响现有的单元测试配置。

    vitest.config.ts
    import { defineConfig } from 'vitest/config';
    
    export default defineConfig({
      test: {
        include: ['e2e/**/*.test.ts'],
        environment: 'node',
        testTimeout: 90_000,
        hookTimeout: 90_000,
        fileParallelism: false,
        sequence: { concurrent: false },
      },
    });

    由于模型调用和设备连接耗时较长,请根据实际用例适当调大 testTimeout 与 hookTimeout。在共享同一台设备时,需关闭文件级并行(fileParallelism: false)并保持用例串行执行(sequence.concurrent: false),同时避免在代码中调用 test.concurrent 或 describe.concurrent。相关配置项详见 Vitest 的 fileParallelism、testTimeout 和 hookTimeout 文档。

    3. 编写测试

    新建 e2e/settings.test.ts,在 beforeEach 中为每个用例创建新的 Agent,并在 afterEach 中销毁:

    e2e/settings.test.ts
    import 'dotenv/config';
    import { afterEach, beforeEach, test } from 'vitest';
    import { agentFromAdbDevice } from '@midscene/android';
    
    let agent: Awaited<ReturnType<typeof agentFromAdbDevice>> | undefined;
    
    beforeEach(async () => {
      agent = await agentFromAdbDevice();
    });
    
    afterEach(async () => {
      const currentAgent = agent;
      agent = undefined;
      await currentAgent?.destroy();
    });
    
    test('scroll through settings', async () => {
      if (!agent) throw new Error('Agent was not initialized');
    
      await agent.launch('com.android.settings');
      await agent.aiWaitFor('The Settings app is visible');
      await agent.aiAct('Scroll down one screen');
      await agent.aiAssert('The Settings app is still visible');
    });

    连接多台设备时,通过 agentFromAdbDevice(deviceId) 指定设备 ID。连接参数详见 Android SDK 文档。

    dotenv/config 用于从项目的 .env 文件加载模型配置(Playground 中的配置不会自动同步到测试进程)。

    当 aiAssert 的断言未通过时,会直接抛出错误,Vitest 随即将其所在用例标记为失败,并继续执行 afterEach 销毁 Agent。你也可以用 aiQuery 等方法提取界面数据,再交给 Vitest 的 expect 进行校验。更多交互与提取方法见 Agent API 参考。

    4. 运行测试与查看报告

    pnpm exec vitest run e2e/settings.test.ts

    运行后,Vitest 会在终端汇总用例的通过或失败状态;Midscene SDK 则默认在 midscene_run/report/ 下生成各 Agent 的执行报告,并在终端打印报告路径。在浏览器中打开生成的 HTML 报告,即可查看各步骤的截图、AI 执行过程和断言结果。

    与 Playwright Test 不同,Vitest 接入直接使用 SDK 内置的报告生成机制,无需额外部署 reporter。Vitest 的测试结果与 Midscene 的 Agent 报告相互独立;如需合并或进一步处理报告文件,请参阅处理报告文件。

    常用方法

    能力分类代表方法说明
    自动规划aiAct用自然语言描述目标,由 Agent 自动规划并执行多步操作。
    即时交互aiTap、aiInput根据描述定位目标元素并执行单步指定操作,不进行多步规划。
    数据提取aiQuery、aiString、aiNumber、aiBoolean从当前界面提取结构化或基础类型数据,供后续代码处理。
    界面断言aiAssert校验当前界面是否符合预期;若不符合则抛出错误。
    条件等待aiWaitFor等待界面出现预期状态;若超时仍未满足则抛出错误。

    在实际使用时,通常先用 aiAct 或即时交互方法完成操作,再用 aiAssert 校验界面结果(操作执行完毕并不代表业务预期一定成立)。各方法的详细参数与返回值请参阅 Agent API 参考。

    生命周期与设备独占

    • 按用例隔离 Agent:每个用例都应创建独立的 Agent。每个 Device 实例只能归属于一个 Agent,调用 agent.destroy() 时会一并清理对应的 Device,切勿在后续用例中复用已销毁的实例。
    • 显式管理应用状态:销毁 Agent 不会重置应用数据或关闭目标应用。每个用例应自行准备好初始界面与测试数据,并在必要时清理状态,确保用例支持单独运行或失败重试。
    • 避免多进程争用设备:Vitest 的串行配置仅约束当前测试进程,其他本地进程、CI 任务或人工操作仍可能争用同一台设备。如需并行运行测试,请为每个执行单元分配独立的设备或桌面会话,并在代码中显式指定连接目标。

    完整的示例工程可参考 Android、iOS、HarmonyOS 和桌面端的 Vitest 示例项目。