引言
小程序的 E2E 测试,长期卡在"开发者工具只有桌面版"这一步。
这是"一天一个开源项目"系列的第 247 篇 文章。今天带你了解的项目是 wxa-runner(open-wxa/wxa-runner)。
做过微信小程序自动化测试的人大概都踩过这个坑:官方的开发者工具只有 Windows 和 macOS 版本,没法塞进 Linux CI;想用 Playwright 这类现代工具,又没有一个能在浏览器里跑的小程序运行环境。H5 方案倒是能跑,但渲染和组件是框架重新实现的,和真机总有差异。
wxa-runner 的思路很干脆:不重写基础库,而是把微信官方基础库原样搬进浏览器,自己只补上微信客户端那层"原生壳"。
你将学到什么
- wxa-runner 要解决什么问题,以及它不解决什么
- 它的核心思路:只实现原生层,复用官方基础库与编译器
- 分层架构与各个包的职责
- 浏览器里怎么跑起逻辑层和渲染层,WXML/WXSS 怎么编译
- 与开发者工具的关系,以及在 Linux CI 上的运行方式
- 已知限制与适用边界
前置知识
- 了解微信小程序的基本结构(逻辑层 / 渲染层、WXML / WXSS、
setData) - 用过 Playwright 或类似的浏览器自动化工具
- 对 uni-app 或 Taro 有所了解更佳
项目背景
项目简介
一句话:让小程序在普通浏览器中借助微信官方基础库运行,用于本地调试和 E2E 自动化测试。
需要先说清楚它的边界:它不取代微信开发者工具,预览、上传、真机调试仍然需要开发者工具。
作者/团队介绍
- 组织账号:open-wxa
- 具体个人作者与贡献者名单:项目页未提供
- 致谢:微信团队(官方基础库与编译器);WEPT(chemzqm/wept),最早验证了在浏览器里运行小程序的可行性
- 创建时间:未提供
项目数据
- ⭐ GitHub Stars: 556
- 🍴 Forks: 73
- 📝 Commits: 184
- 📦 版本: v0.8.0
- 📄 License: MIT(注意:内置基础库
wxa-baselib是微信官方代码,版权归腾讯,不适用 MIT) - 📚 文档: open-wxa.github.io/wxa-runner
- 🏷️ Topics: e2e-testing、mini-program、playwright、simulator、taro、uni-app、weapp
主要功能
核心作用
把小程序的编译产物直接放进浏览器运行,渲染、组件、setData、生命周期都由官方基础库执行,而不是像 H5 那样由某个框架重新实现。这样得到的行为更接近真机,同时又能被浏览器工具链驱动。
使用场景
-
无头 E2E 测试
- 基于 Playwright 在浏览器内测试,无需远程控制开发者工具;CI 中可以无头运行。
-
Linux CI 中的小程序测试
- 在 GitHub Actions 的 Linux 机器上运行,不需要安装开发者工具。
-
本地调试
- 用 Chrome DevTools 打断点、查看网络请求和 DOM,保存后热更新,页面栈和全局状态保留。
-
绕过登录等障碍
- 在 setup 中预置登录态、mock 原生 API、拦截请求(如
wx.request),无需改业务代码。
- 在 setup 中预置登录态、mock 原生 API、拦截请求(如
-
uni-app / Taro 项目的测试
- 支持它们编译出的
mp-weixin目录。
- 支持它们编译出的
快速开始
启动命令:
bash
npx wxa-runner dev ./dist/dev/mp-weixin # 原生小程序目录,或 uni-app / Taro 编译产物
# → http://localhost:3000
可以通过地址参数直达某个页面,例如:
text
http://localhost:3000/?path=pages/detail/detail&id=1
文档站首页没有给出完整的安装步骤、setup 与 Playwright 的代码示例,具体用法请查阅文档站的"启动前准备(setup)""在 Playwright 中测试""在 CI 中运行""命令与环境变量"等子页面。本文不替它们编造示例代码。
核心特性
-
与真机一致的运行方式
- 直接运行编译产物,渲染、组件、
setData、生命周期都由官方基础库执行。
- 直接运行编译产物,渲染、组件、
-
无头 E2E
- 基于 Playwright,可断言页面 data 和发出的请求。
-
Chrome DevTools 调试
- 断点、网络、DOM 都能看,保存后热更新。
-
启动前 setup
- 预置登录态、mock 原生 API、拦截请求。
-
借用开发者工具登录态
- 在装有开发者工具的机器上,可借用其登录态获取真实 code、手机号,并访问服务市场和插件页面。
-
兼容主流跨端产物
- 原生小程序、uni-app、Taro。
项目优势
文档站有一页专门讲"与 miniprogram-automator 的选择",页面首页没有展开,下面是我基于其设计方式做的定性比较,仅供参考:
| 维度 | wxa-runner | 依赖开发者工具的方案(如 miniprogram-automator) | H5 / 框架重新实现 |
|---|---|---|---|
| 运行环境 | 普通浏览器 | 需要桌面版开发者工具 | 浏览器 |
| Linux CI | 可无头运行,无需开发者工具 | 受限于开发者工具只有 Windows / macOS | 可以 |
| 渲染一致性 | 官方基础库执行 | 官方 | 框架自行实现,可能有差异 |
| 驱动方式 | Playwright | 远程控制开发者工具 | 取决于方案 |
为什么值得关注?
- 它解决的是小程序工程化里一个很实际的缺口:能放进 CI 的、尽量贴近真机的运行环境。
- "复用官方、只补原生层"的取舍,让它不用追着基础库的每个版本重写渲染。
- 它对自己的边界很坦诚,限制清单写得很具体。
项目详细剖析
核心思路:只实现"原生层"
小程序运行时可以粗分为两部分:微信官方提供的基础库、编译器、桥协议 ,以及微信客户端提供的原生层(消息桥、页面栈、原生 API、导航栏等原生 UI)。
wxa-runner 的选择是:前者全部复用官方,只自己实现后者。
分层架构
text
应用层:cli | playwright fixture
服务层:core/server(开发服务器、代理、监听、热更新分流)
构建层:project / compiler / extractor
运行时:core/runtime(host 宿主 + frame 注入脚本)
└─ 官方基础库(WAService.js / WAWebview.js)
各包的职责
| 包 | 职责 |
|---|---|
| extractor | 找到开发者工具目录,解析 .wxvpkg,读取 app.asar,把基础库解包缓存到 ~/.wxa-runner/baselib |
| compiler | 封装 wcc(WXML / WXS)与 wcsc(WXSS),优先用开发者工具自带的,否则用 miniprogram-compiler |
| core | project(解析项目、生成产物、__wxConfig)、server(DevServer、监听、热更新、代理、插件)、runtime(浏览器端) |
| cli | 命令行 wxa-runner |
| playwright | 封装 Playwright fixture、匹配器、Node 侧 mock 重放 |
仓库里还有 examples(showcase、hello-world、custom-tabbar、subpackages、worker 等)和 tests(unit、integration、e2e、playwright)。技术栈包括 TypeScript、pnpm workspace、Express、ws、chokidar、esbuild、tsup、Vitest 与 Playwright。
浏览器里是怎么把小程序跑起来的
逻辑层与渲染层。 逻辑层和渲染层各是一个同源 iframe:逻辑层隐藏,渲染层每个页面一个,URL 与页面路径一致,因此相对资源路径能正常解析。
先于基础库注入的 frame.js 负责这些事:
- 预置全局变量
- 提供
WeixinJSBridge(invoke / publish / subscribe / on) - 模拟设备尺寸和竖屏
- 把鼠标事件转成触摸事件
- 提前注入内置组件样式
选择 iframe 而不是 Web Worker ,是因为基础库需要 window / document,而且同源 iframe 能直接调用宿主,所以像 getStorageSync 这样的同步 API 也能同步返回。
宿主只维护页面栈并派发 onAppRoute,页面生命周期(onLoad / onShow / onReady)全部由基础库驱动。页面栈上限 10 层,路由操作串行,tabBar 页面实例做缓存。
编译 WXML / WXSS。
wcc以子进程 方式运行。原因是原生模块wcc.node在同一进程里反复调用会随机std::bad_alloc崩溃。- 所有 wxml 和 wxs 一次编译成一个
$gwx:渲染层用完整版,逻辑层用裁剪版,缺少裁剪版会报节点创建期望不匹配。 wcsc输出的结果中,comm是公共运行时,其余每个页面或组件对应一个setCssToHead,rpx 换算由 wcsc 生成的运行时完成。- 增量构建:只改 JS 时不重编模板和样式,三类编译并行,并发请求共享同一次构建。
- 逻辑层每个 JS 文件一个
define脚本,行号与源文件一致;ES module 经 esbuild 转成 CommonJS 并带 source map,所以断点调试对得上源码。
模拟 wx API 与原生组件。
- 原生 API 由注册表管理,涵盖 storage、system、network、routes、ui、media、audio、map、picker、webView、fs、subpackages、misc 等模块。能同步得到结果的调用同步回调;未实现的 API 默认返回 ok 并提示一次。
- 渲染层上报的平台是
devtools,所以大多数原生组件直接用基础库为开发者工具准备的网页实现,canvas、video、map、camera、audio、input 等可用。picker 由宿主弹出选择器,web-view 用 iframe 嵌入。 - 广告、云开发、支付这类伴生 SDK 没有加载机制,只在
wx层放占位桩,让业务走失败分支。 - Worker 使用浏览器的 Web Worker,由宿主实现
WeixinWorker的两端。
热更新。 只改 wxss,或者页面 / 组件自己的 wxml / js 时热更新;其他改动整页刷新并恢复页面栈。Page() 页面把新方法换到活实例上,Component() 页面和组件交给基础库重新挂载。
与测试相关的启动顺序
宿主会挂载 window.__wxa_test_api__,启动顺序为:测试 API → setup → __wxa_before_launch__ → 启动小程序。setup 在逻辑层创建之前执行,并且只传递以 WXA_ 开头的环境变量。这正是"不改业务代码就能预置登录态、mock 原生 API"的底层机制。
与开发者工具的关系
wxa-runner 复用开发者工具的基础库(.wxvpkg)、编译器(wcc / wcsc)和行政区划数据,抛弃桌面壳。
没有开发者工具时(Linux、CI):
- 基础库使用内置的
@open-wxa/wxa-baselib(3.7.0) - 编译器使用
miniprogram-compiler,仅支持 x86-64,内核较旧,模板运行时有少量差异 - 地区选择器不可用
同一版本的基础库优先用开发者工具的,可通过 WXA_DEVTOOLS_PATH、WXA_VENDOR_DIR、WXA_WCC / WXA_WCSC 覆盖路径。
基础库版本方面:默认使用不高于 3.7.0 的最新兼容版本;3.7.1 起,业务代码在原生 WeixinJSContext 创建的隔离子上下文中运行,需要用 --baselib 或 WXA_BASELIB 显式指定版本;3.8 起框架拆到 WAServiceMainContext.js / WASubContext.js。
几个关键设计取舍
| 取舍 | 原因 |
|---|---|
| 不直接用开发者工具 | 只有 Windows / macOS 版,难以放进 CI 和无头浏览器,也无法被 Playwright 驱动 |
| 用 iframe 而非 Web Worker | 基础库需要 window / document,同源 iframe 还能支持同步 API |
| 不重写基础库 | 工作量大且难以保证一致;最初自己模拟的版本只能支持很有限的能力 |
| 内置基础库放独立仓库 | 版权归腾讯,不适用 MIT,作为可选依赖安装,失败不影响主体 |
| 编译器用子进程 | 避开原生模块的随机崩溃 |
| Worker 同步接口直接失败 | 开发者工具靠 SharedArrayBuffer 阻塞等待,那需要 COOP / COEP 跨源隔离,会让跨域资源加载失败 |
已知限制
这一部分写得越具体,越说明项目对自己的边界很诚实:
- 基础库超过 3.7.0 属于未验证范围
- 未覆盖或能力受限的组件:live-player(不能播放)、live-pusher、voip-room、open-data(渲染为空)、ad(走失败分支)、xr-frame
- web-view:设置了 X-Frame-Options 或 CSP frame-ancestors 的网站无法嵌入;网页内的
wx.miniProgram接口不可用;binderror不会触发 - map:需要联网,部分方法(
toScreenLocation、地面覆盖物、室内图等)无效 - 文件系统:
saveFile指定filePath会失败,saveFileSync未实现;临时文件存在开发服务器上,重启后失效 - 网络代理只用于本机开发环境,基础库自己的请求直接按失败返回
- 在无开发者工具的环境里,编译器仅支持 x86-64,且与真机编译器存在少量差异
如何评价它的价值
- 它测的是"逻辑和交互",不是"真机像素"。 浏览器里的渲染、原生组件与真机总有差异,适合验证业务流程、页面 data、请求,而不是做视觉回归。
- 覆盖面取决于你的小程序用了哪些 API。 重度依赖直播、AR、广告、云开发的页面,需要自己评估。
- 版本跟随是长期成本。 基础库超出 3.7.0 属于未验证范围,随着微信基础库演进,项目需要持续跟进。
- 项目页没有给出路线图、配置详解和正式的同类对比,深度使用要依赖文档站和
PROJECT_DESIGN.md。
项目地址与资源
官方资源
- 🌟 GitHub : github.com/open-wxa/wx...
- 📚 文档 : open-wxa.github.io/wxa-runner/
- 📦 内置基础库 : github.com/open-wxa/wx...
- 🐛 Issue Tracker : github.com/open-wxa/wx...
相关资源
- WEPT:最早验证了浏览器运行小程序的可行性
- 仓库中的
PROJECT_DESIGN.md:架构与设计取舍的详细说明 - 文档站"与 miniprogram-automator 的选择"一页
总结与展望
核心要点回顾
- 思路清晰:基础库、编译器、桥协议复用微信官方,只自己实现原生层,比重写基础库稳得多。
- 面向 CI:用 Playwright 驱动,Linux 上无需开发者工具即可无头运行。
- 贴近真机 :渲染、
setData、生命周期都由官方基础库执行。 - 边界坦诚:限制清单具体,也明确说明不取代开发者工具。
适用人群
- 想把小程序 E2E 测试放进 Linux CI 的团队
- 使用 uni-app / Taro 开发小程序、需要自动化回归的开发者
- 对"如何在浏览器中复用官方运行时"这类工程问题感兴趣的读者
学习建议
先在 examples/hello-world 上跑一遍 npx wxa-runner dev,用 Chrome DevTools 熟悉调试体验;再读 PROJECT_DESIGN.md 理解 host / frame / 基础库的分工;最后按文档站的 setup 与 Playwright 页面,给自己项目写第一个带登录态 mock 的 E2E 用例,并在 CI 里无头跑通。
探索 PrimeSkills ------ 精选 AI agent 和技能工具,每一个都经过真实工作流验证。没有炒作,只有真正好用的工具。
访问我的个人主页,获取更多见解和有趣的产品。