vitest-environment-mp-auto
v2.0.0
Published
Vitest 自定义环境,将 mp-auto + Midscene AndroidAgent 集成到 Vitest 生命周期中。
Readme
vitest-environment-mp-auto
Vitest 自定义环境,将 mp-auto + Midscene AndroidAgent 集成到 Vitest 生命周期中。
- globalSetup 全局只执行一次
mp-auto start,等待小程序 WS 连接就绪后才开始跑用例 - environment 创建并复用
AndroidAgent,连同 mp-auto HTTP 辅助函数一起注入到全局变量 - 第一版强制串行(
maxWorkers: 1),因为安卓设备和微信只有一个
安装
npm install vitest-environment-mp-auto --save-dev前置:需要全局可用的 mp-auto CLI(通过 @tencent/mp-auto 安装)和已连接的 Android 设备。
配置
vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// 使用 mp-auto 环境(自动解析 vitest-environment-mp-auto 包)
environment: 'mp-auto',
// globalSetup 确保 mp-auto daemon 就绪
globalSetup: 'vitest-environment-mp-auto/global-setup',
// 必须开启:environment 拦截 describe/it/expect 并自动 recordToReport
globals: true,
// 测试树注入:跑完后在 midscene html 左侧注入 pass/fail/skip 树
reporters: ['default', 'html', 'vitest-environment-mp-auto/reporter'],
// 串行执行(单设备限制)
fileParallelism: false,
pool: 'forks',
isolate: false,
maxWorkers: 1,
// UI 自动化需要较长超时
hookTimeout: 120_000,
testTimeout: 120_000,
// 传递给 environment + globalSetup 的配置
environmentOptions: {
mpAuto: {
projectPath: '/path/to/miniprogram', // 小程序项目路径(传给 mp-auto start)
port: 9400, // mp-auto daemon 端口,默认 9400
// serial: 'xxx', // 指定 Android 设备序列号(多设备时)
// contextPath: './', // .context/ 所在路径(默认 cwd)
},
},
},
});环境变量(兼容回退)
推荐通过 environmentOptions.mpAuto 配置所有参数。环境变量仅作为兼容回退:
| 变量 | 对应配置项 | 说明 |
| --- | --- | --- |
| MP_AUTO_PROJECT_PATH | mpAuto.projectPath | 小程序项目路径 |
| MP_AUTO_PORT | mpAuto.port | daemon 端口(默认 9400) |
| MP_AUTO_CONTEXT_PATH | mpAuto.contextPath | .context/ 所在路径 |
| ANDROID_SERIAL | mpAuto.serial | Android 设备序列号 |
优先级:environmentOptions.mpAuto.* > 环境变量 > 默认值。
如果 mp-auto 已经在运行且有小程序客户端连接,globalSetup 会跳过启动,无需设置项目路径。
类型支持
v1.1.0 起全局类型已合并进主入口,零配置即可获得
agent、describe、it、expect以及所有 mp-auto helper 的类型提示。
只要项目 tsconfig.json 能解析到 vitest-environment-mp-auto(默认都能),主入口 dist/index.d.ts 的 /// <reference path="./globals.d.ts" /> 会自动把全局声明带入项目。
通常这一条就够了——它在 vitest.config.ts 里本来就需要:
import 'vitest-environment-mp-auto'; // 或直接使用 environment: 'mp-auto'如果你关闭了 includes/types 的默认值、或者使用 "types": [...] 白名单限制了类型加载,需要显式补一条:
{
"compilerOptions": {
"types": ["vitest-environment-mp-auto"]
}
}兜底方案(适用于极端定制的 tsconfig)—— 在任意一个 .d.ts 文件(如 vitest-env.d.ts)或测试文件顶部加三斜线引用:
/// <reference types="vitest-environment-mp-auto" />⚠️ 不要再用
vitest-environment-mp-auto/types/global,该路径仅为兼容旧版本保留,将来会移除。
全局 API
environment 注入以下全局变量,测试文件中无需任何 import,直接使用。
配合 globals: true,vitest API(describe/it/expect/beforeEach/afterEach 等)也会被 environment 自动拦截并包裹 agent.recordToReport(),生成 Midscene HTML 报告。
agent
AndroidAgent 实例(来自 @midscene/android),全进程共享,提供 Midscene UI 自动化能力:
await agent.aiTap('提交按钮');
await agent.aiAssert('显示成功提示');
await agent.aiWaitFor('页面加载完成', { timeoutMs: 15_000 });
await agent.aiInput('搜索框', { value: 'hello' });
const title = await agent.aiQuery('string, 页面标题文字');mp-auto 辅助函数(自动 recordToReport)
| 函数 | 说明 |
| --- | --- |
| clearMocks() | 清除所有 mock 规则 |
| clearRequests() | 清除已录制的请求 |
| relaunch(url) | wx.reLaunch 到指定页面 |
| navigate(url) | wx.navigateTo 到指定页面 |
| mockRequest(url, response, opts?) | 添加单条 mock 规则 |
| mockRequests(rules) | 批量添加 mock 规则 |
| getRequests(urlFilter?) | 查询已录制的请求 |
| setupPage(pageUrl, pageId, scenarioId) | 加载页面场景 mock + 导航(一步到位) |
| loadPageScenario(pageId, scenarioId) | 加载页面场景的结构化 mock |
| loadMock(...args) | 从 .context/business/mocks/ 加载响应变体 |
| setContextPath(path) | 设置 .context/ 所在的仓库路径 |
测试文件示例
describe('首页', () => {
beforeEach(async () => {
await clearMocks();
await clearRequests();
});
it('TC-IDX-001: 页面正常加载', async () => {
await setupPage('/pages/index', 'module/index', 'S1');
await agent.aiAssert('页面标题显示"首页"');
await agent.aiAssert('底部导航栏可见');
});
it('TC-IDX-002: 点击提交后显示成功', async () => {
await setupPage('/pages/index', 'module/index', 'S1');
await agent.aiTap('提交按钮');
await agent.aiAssert('显示"提交成功"提示');
const reqs = await getRequests('/api/submit');
expect(reqs).toHaveLength(1);
});
it('TC-IDX-030: [Mock] 接口异常时显示错误提示', async () => {
await mockRequest('/api/data', { code: -1, msg: '系统维护中' });
await relaunch('/pages/index');
await agent.aiAssert('"系统维护中"提示文字可见');
});
});工作原理
vitest run
│
├── globalSetup.setup()
│ ├── fetch /api/status → 检查 mp-auto 是否已就绪
│ ├── 未就绪 → execSync("mp-auto start <projectPath>")
│ │ └── daemon 启动 → 注入 bridge → 预览二维码 → ADB 扫码 → WS 连接
│ └── 就绪确认 (clients > 0)
│
├── environment.setup() ← 每个测试文件执行前
│ ├── 创建 AndroidAgent(首次)或复用已有实例
│ ├── 注入 global.agent + 辅助函数
│ └── 拦截 describe/it/expect:describe/it 不再截图;
│ expect 只在失败时截图并附带 describe→it 路径 + 错误摘要
│
├── [串行执行所有测试文件]
│ └── 每个 it 启动时记录 executions 索引,后面写入 bridge.json
│
├── environment.teardown() ← 最后一个文件结束后
│ ├── drainPendingRecords + agent.destroy()(finalize html)
│ └── 写 midscene_run/.vitest-mp-auto/bridge.json
│ { reportFile, caseAnchors, totalExecutions }
│
├── reporter.onTestRunEnd() ← 主进程
│ └── 读 bridge.json → 把 pass/fail/skip 树 + 客户端 bootstrap
│ 脚本注入到 midscene html 的 </html> 之前
│
└── globalSetup.teardown()
└── (daemon 保持运行,不自动 stop)测试树面板(reporter)
配置 reporters: ['default', 'html', 'vitest-environment-mp-auto/reporter'] 后,vitest 跑完后 midscene html 左侧会多出一个折叠面板:
- 顶部统计
N tests · X pass · Y fail · Z skip · 总耗时 - 按
file → describe → it层级渲染,每级聚合 PASS/FAIL/SKIP - 工具栏按钮:
−全部折叠、+全部展开、!只显示失败用例 - 点击某个用例:滚动到右侧 Execution 步骤区该用例的第一步并高亮
- 失败用例右侧有
error按钮,点击展开完整错误栈
注入是幂等的:同一个 html 再次打开 reporter 时会替换旧的注入块(使用 <!--VITEST_MP_TREE_BEGIN--> 哨兵)。
本地开发
改完源码后重新构建并在消费仓库里刷新:
# 在本包目录
pnpm build
# 在消费仓库(例如 apps/points)
pnpm install --force # 或者 pnpm update vitest-environment-mp-auto如果用 pnpm link 到本地:
# 在本包目录
pnpm build && pnpm link --global
# 在消费仓库
pnpm link --global vitest-environment-mp-auto