2026年10月25日 · 阅读 —
开源解读:它把小程序的 E2E 测试搬进了普通浏览器
开源解读:它把小程序的 E2E 测试搬进了普通浏览器
先把能跑的放最前面:想在一个普通浏览器里看到并操作某个微信小程序页面,可以这么做——
npx wxa-runner dev ./dist/dev/mp-weixin # 原生小程序目录,或 uni-app / Taro 编译产物
# → http://localhost:3000
跑起来的 localhost:3000 就是一个小程序界面,像打开普通网页一样;Chrome DevTools 能打断点、看网络请求、看 DOM,保存后热更新。这个工具叫 open-wxa/wxa-runner,GitHub 上目前 580+ star、79 fork,MIT 协议。
它不替代微信开发者工具——预览、上传、真机调试仍然要回开发者工具。它补的是”本地调试 + 自动化测试”这块。官方文档里,围绕它能做的事列了五个主题,正好是测试工程师每天都碰的场景:写 E2E 测试、上 CI、跑 uni-app、跳过登录和 mock、和官方 automator 怎么选。下面按这个顺序展开。
先看它一行命令跑了什么
./dist/dev/mp-weixin 是 uni-app 的常见编译产物目录;原生小程序直接指到 project.config.json 所在目录就行。它是 JavaScript/TypeScript 的 monorepo,用 pnpm 管理,核心包拆成 cli(命令行)、compiler(编译器)、core(核心运行)、extractor(提取)、playwright(测试集成)几个部分。这行命令背后,先确认这件事最要紧:
它和你认识的小程序,是同一套代码。
为什么说它是同一套代码
这是它区别于”把小程序编译成 H5 再测”的关键,也是 README 反复强调的:它用的是微信官方基础库,不是 H5 那种框架重写。
uni-app、Taro 都能把项目编译成 H5,再用 Playwright 测 H5 页面——但这时运行的是框架为 H5 重新实现的组件和 API,和小程序里跑的不是同一套代码,setData、WXSS、自定义组件相关的问题可能测不出来;原生小程序甚至没有 H5 版本。wxa-runner 相反:它复用微信官方基础库,渲染、组件、setData、生命周期全是官方代码,跑的是 mp-weixin 编译产物,和微信里运行的是同一份。所以页面行为跟真机对得上,测试结果才有参考价值。
支持范围上,原生小程序直接跑;uni-app 和 Taro 等框架跑它的编译产物。基础库已验证 2.32.3、3.3.4、3.5.8、3.7.0 等版本,它会把”还没实现”的 API 诚实地记进错误列表,让测试自己发现,不装作全支持。
用 Playwright 写 E2E 测试
从零到跑通,完整走一遍(以原生小程序为例):
1. 安装
pnpm add -D @playwright/test @open-wxa/wxa-runner-playwright
用例在本机安装的 Google Chrome 里运行;没装 Chrome 时,跑一次 npx playwright install chrome。
2. 建配置文件 playwright.config.ts,放在 project.config.json 所在目录
import { defineConfig } from '@playwright/test';
import type { WxaOptions } from '@open-wxa/wxa-runner-playwright';
export default defineConfig<WxaOptions>({
testDir: 'e2e',
use: {
channel: 'chrome'
}
});
3. 写第一个用例 e2e/index.spec.ts。注意 test 和 expect 要从 @open-wxa/wxa-runner-playwright 导入,不是 @playwright/test——这样用例里才有 miniProgram,expect 才有 toHavePath 等断言:
import { test, expect } from '@open-wxa/wxa-runner-playwright';
test('首页能打开', async ({ miniProgram }) => {
await miniProgram.open('pages/index/index');
await expect(miniProgram).toHavePath('pages/index/index');
});
4. 运行
npx playwright test # 无头运行全部用例
npx playwright test --headed # 打开浏览器窗口,看着它跑
npx playwright test --ui # 图形界面,一步步回看
这套就是整个流程的主干;下面看它具体能断言什么、怎么打桩。
自动化接进来用的是 Playwright(浏览器自动化测试框架)的 miniProgram fixture,用例写得很像在读需求:
test('点击「详情」进入详情页', async ({ miniProgram }) => {
await miniProgram.open('pages/index/index');
await miniProgram.current.locator('#detail').click();
await expect(miniProgram).toHavePath('pages/detail/detail');
});
打开页面 open(...)、点按钮 current.locator(...)、断言跳转 toHavePath(...)、断言页面状态 toHavePageData({ status: 'ok' })——这些断言会自动重试到超时,不用手写 waitFor。还能断言小程序发出的请求:
await miniProgram.clearRequests();
await miniProgram.current.locator('#save').click();
const req = await miniProgram.waitForRequest('POST **/api/profile');
expect(req.body).toEqual({ name: '张三' });
登录、支付这类测不动的环节,直接在测试里 mock 掉:
await miniProgram.mock('login', { code: 'test_code' }); // 假登录取 code
await miniProgram.route('POST **/api/orders', { // 拦下单接口,返回假订单
status: 201, body: { id: 'o-1' }
});
await miniProgram.open('pages/cart/cart');
甚至能在 route 的函数里写断言,检查请求体参数对不对——等于把”接口打桩”直接并进了 E2E。
原生小程序和 uni-app 的断言习惯不同:uni-app(Vue 3)的页面状态在 Vue 实例里(ref、reactive),交给小程序的是编译器生成的渲染数据,字段名通常是 a、b 这类短名字,和源码变量名对不上,所以 toHavePageData 读不到源码里的状态。官方文档的建议是:uni-app 项目断言页面上的文字和元素(getByText(...)、locator('#id')),以及跳转和发出的请求,不要断言页面 data。
uni-app 项目怎么接
wxa-runner 只认编译产物目录,先确认它在哪:
| 项目来源 | 开发(监听构建) | 发行 |
|---|---|---|
| 命令行创建 | npm run dev:mp-weixin → dist/dev/mp-weixin | npm run build:mp-weixin → dist/build/mp-weixin |
| HBuilderX 创建 | unpackage/dist/dev/mp-weixin |
跑起来分两步:先开 uni-app 的监听构建,再起编译产物目录——
# 终端 1:uni-app 的监听构建
npm run dev:mp-weixin
# 终端 2
npx wxa-runner dev ./dist/dev/mp-weixin --open
改了源码后 uni-app 重新构建,wxa-runner 自动热更新。uni-app 页面用 Component() 构造,改某个页面的 JS 后只重新挂载这一页,页面栈、globalData、Pinia 等全局状态都保留。
一个注意点:setup 文件不要放产物目录——dist 由 uni-app 生成,重新构建可能被清空。官方做法是放在源码根目录,启动时用 --setup 指定:
npx wxa-runner dev ./dist/dev/mp-weixin --setup ./wxa.setup.ts
写用例时,playwright.config.ts 里的 wxaProject 指向编译产物目录。测试前先构建再跑,官方建议在 package.json 配一个脚本:
{ "scripts": { "test:e2e": "npm run build:mp-weixin && playwright test" } }
限制也摆明:真实项目只验证过 Vue 3 的 uni-app;测的是 mp-weixin 产物,只覆盖微信平台,#ifdef 条件编译里其他平台的代码不会运行。
上 CI 无头跑
微信开发者工具只有 Windows、macOS 版,靠它跑自动化,CI 得准备一台装好开发者工具的机器。wxa-runner 在浏览器里跑,没有开发者工具时,用随依赖安装的内置基础库 3.7.0 和微信官方 npm 包 miniprogram-compiler 里的编译器,GitHub Actions 的 Linux 机器上就能无头跑。wxa-runner 自己的测试就是这么在 CI 里跑的。原生小程序一份最小配置:
# .github/workflows/e2e.yml
jobs:
e2e:
runs-on: ubuntu-latest # 自带 Google Chrome,要 x86-64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, cache: npm }
- run: npm ci
- run: npx playwright test
关键点:@open-wxa/wxa-runner-playwright 装在 devDependencies,npm ci 时会把内置基础库和编译器一起装上。uni-app 等框架项目,测试前先 npm run build:mp-weixin 构建出产物,wxaProject 指向它;playwright.config.ts 用 channel: 'chrome',GitHub Actions 的 Linux 自带 Chrome。
GitLab CI、Jenkins 也可以,机器要满足:Linux x86-64、Node.js ≥ 18;装依赖时有 git、能访问 github.com(内置基础库从 GitHub 安装);装有 Chrome(没有就跑 npx playwright install --with-deps chrome)。
限制:没有开发者工具时,只支持 x86-64;基础库只有内置 3.7.0;编译器版本较旧,模板运行时有少量差异(如单页最多 16000 个节点),地区选择器不可用;--devtools-login 用不了(开发者 ticket 很快过期),登录用 setup 的 mock 和 network.routes 跳过。
排查:本地通过、CI 失败,常见原因是本地装了开发者工具,用的是它下载过的基础库和自带编译器,和 CI 不同。先用和 CI 一样的基础库版本跑一遍验证:
WXA_BASELIB=3.7.0 npx playwright test
仍只在 CI 失败,多半是编译器差异或网络环境不同——没被 mock 的请求在 CI 上真的会发出去。
和官方 automator 怎么选
要写小程序 E2E,另一条主流路是微信官方的 miniprogram-automator:用 Node.js 脚本控制开发者工具(也能通过真机调试控制手机),跳转页面、读页面 data、点元素、mock wx 方法。wxa-runner 不控制开发者工具,而是在普通 Chrome 里用官方基础库跑小程序,用 Playwright 写测试。两条路都能做 E2E,场景不同,还能一起用。
| miniprogram-automator | wxa-runner | |
|---|---|---|
| 维护方 | 微信官方 | 社区开源项目,0.x 阶段 |
| 运行在 | 开发者工具模拟器 / 真机 | 普通 Chrome,可无头 |
| 开发者工具 | 需要,且要开「CLI/HTTP 调用」 | 不需要,没装时用内置基础库和编译器 |
| 操作系统 | 工具只有 Windows / macOS | Win / macOS / Linux,无工具时只 x86-64 |
| 真机 | 支持(miniProgram.remote()) | 不支持 |
| mock 原生 API | mockWxMethod | miniProgram.mock(...),可写 setup 或用例 |
| 截网络请求 | 用 mockWxMethod 替换 wx.request | 按 URL 规则拦截,记录请求、可断言请求体 |
miniprogram-automator 一列取自官方文档。怎么选,官方给了一个很实用的矩阵:
用 automator 更合适:要真机跑自动化;用了插件自定义组件,或要测蓝牙、Worker 网络请求等 wxa-runner 还不支持的能力;要用多账号调试、体验评分这类开发者工具功能;已经有装好工具的 Windows/macOS 机器、用例也已用 automator 写好。
用 wxa-runner 更合适:要在 Linux CI 无头跑,不想维护一台装开发者工具的机器;想用 Playwright 的 UI 模式、trace、并行、自动重试断言;测试要跳过登录、mock 大量接口(setup 预置登录态、按 URL 拦截并断言请求体);开发时用 Chrome DevTools 调试 + 热更新。
一起用才是常见形态:日常提交在 Linux CI 上用 wxa-runner 跑,发版前用 automator 在开发者工具或真机上回归;预览、上传始终在开发者工具里做。
用过 automator 的同学想切过来,官方给了对照表,最常用的几个:
| miniprogram-automator | wxa-runner |
|---|---|
miniProgram.reLaunch('/pages/index/index') | miniProgram.open('pages/index/index') |
(await page.$('#submit')).tap() | miniProgram.current.locator('#submit').click() |
page.data() | miniProgram.pageData() |
miniProgram.mockWxMethod('getLocation', r) | miniProgram.mock('getLocation', r) |
miniProgram.mockWxMethod('request', r) | miniProgram.route('GET **/api/feed', { body: {...} }),只拦匹配的请求 |
miniProgram.close() | 不用写,用例结束 fixture 自动清理 |
选择前要确认的限制:不替代开发者工具(预览、上传、真机调试还要用它);默认 3.7.0 及以下基础库,更新的要用 --baselib/WXA_BASELIB 指定;导航栏、tabBar、原生 API 由 wxa-runner 自己实现,可能和真机有差异,发布前仍要在真机验证;真实项目只验证过 uni-app(Vue 3)。
跳过登录、mock 原生 API 和 wx.request
浏览器里拿不到真实的登录 code、定位、用户信息,这是所有小程序测试都要面对的坎。wxa-runner 用 setup 文件在小程序启动前(App.onLaunch 之前)做三件事,不用改业务代码:
- 预置 Storage:写入 token,小程序一启动就是”已登录”
- 替换原生 API:让
wx.getLocation返回固定坐标 - 拦截请求:让登录接口直接返回 token,不发到后端
export default defineSetup({
storage: { token: 'test-token' },
mock: { getLocation: { latitude: 31.23, longitude: 121.47 } },
network: {
routes: { 'POST **/api/auth/login': { body: { code: 200, data: { token: 'test-token' } } } }
}
});
四步按 storage → mock → network → run 顺序执行,每次打开、刷新页面都会重跑一遍。不 mock 时,登录返回假 code(wxa_mock_code_ 开头,真实后端不认)、定位返回北京天安门坐标、支付默认失败要用到时必须 mock。注意 setup 文件和 WXA_ 环境变量都会发到浏览器里,只放测试用的 token,不要放真正的密钥。
对测试工程来说,它补了哪一块
几个点能直接对应到测试团队的日常动作。
它把”检查运行时错误”默认开成了用例的守卫。 每个用例结束时,fixture 会检查小程序有没有出错——逻辑层/渲染层的运行时错误,以及调用了还没实现的 wx API——只要出错用例就失败。这对测试的价值很直接:那些”没实现、所以默认返回成功以免打断业务”的 API,如果业务真调用了,测试会替你揪出来,而不是让错误静默溜走。这正是”presence ≠ working”(能用不等于对)的测试纪律。
它把状态隔离这件事也安排好了。 README 里说明:每个 Playwright worker 启动一个开发服务器、小程序只编译一次;每个用例用一个新的浏览器上下文,Storage、用户文件、页面栈互不影响。对要并行跑一坨用例的测试工程,这是能不能在 CI 上稳住的关键。
直接定位任意页面也是个省事的点。 地址加 ?path=pages/detail/detail&id=1 就能直接打开指定页面,开发和测试都能一步到位,不用一路点进去。
落回现实:它补的是一块,不是全部。要发版、要真机验证,仍得回微信开发者工具;蓝牙这类设备能力它现在也不碰。对”想给小程序补上可上 CI 的 E2E、又不想被工具模拟器绑死”的测试团队,它是个低成本、能落地的起点——一行命令跑起来,setup 搭好假环境,剩下的就是写用例。
仓库:https://github.com/open-wxa/wxa-runner 文档:<https://open-wxa.github.io/wxa-runner/
#小程序 #E2E 测试 #Playwright #微信 #测试工程 #开源工具 #CI #uni-app #wxa-runner