2026年10月25日 · 阅读 —

开源解读:它把小程序的 E2E 测试搬进了普通浏览器

测试与评测AI 工程实践

开源解读:它把小程序的 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-weixinnpm 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-automatorwxa-runner
维护方微信官方社区开源项目,0.x 阶段
运行在开发者工具模拟器 / 真机普通 Chrome,可无头
开发者工具需要,且要开「CLI/HTTP 调用」不需要,没装时用内置基础库和编译器
操作系统工具只有 Windows / macOSWin / macOS / Linux,无工具时只 x86-64
真机支持(miniProgram.remote())不支持
mock 原生 APImockWxMethodminiProgram.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-automatorwxa-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