为了在 Linux CI 里跑小程序 E2E,我把微信官方基础库搬进了浏览器:wxa-runner 开源实践

GitHub 仓库 :github.com/open-wxa/wx...

文档站点 :open-wxa.github.io/wxa-runner/

适用场景:本地日常快速开发调试、Linux / Docker CI 流水线中的无头 E2E 自动化测试。

一、 背景与痛点:为什么要做这个工具?

在微信小程序开发(无论是原生、uni-app 还是 Taro)的日常流中,有两个长期困扰团队的工程痛点:

1. 持续集成(CI)里的端到端测试是个难题

官方提供了 miniprogram-automator,可以通过 Node.js 脚本遥控微信开发者工具或真机。但在持续集成(CI)环境中落地时,往往会遇到阻碍:

  • 开发者工具只有 Windows 和 macOS 版本,无法在 Linux 容器/服务器中原生无头运行;
  • 虽然后端或跨端框架(uni-app/Taro)支持编译为 H5 进行测试,但 H5 运行的是由 Web 框架重新实现的组件和 API,底层并不是小程序的双线程与 setData 机制,很多在小程序环境特有的 bug(如渲染时序、WXSS 差异、自定义组件通信)在 H5 下根本测不出来。

2. 本地开发与调试时的体验损耗

  • 开发者工具本身较重,对机器资源消耗较大;
  • 调试代码时,断点很容易跟进压缩混淆后的官方 WAService.js 中;
  • 调试深层页面时,一旦触发整页重载,页面栈丢失,需要反复从首页手动点入;
  • 遇到需要微信登录或定位的页面,往往需要临时在业务代码里硬编码 mock 数据。

为了在不维护 macOS 机器的前提下在 Linux CI 上跑小程序真实 E2E ,同时让本地调试更轻量,我开发并开源了 wxa-runner。


二、 核心原理:它与 H5 模拟有何不同?

很多同学可能会好奇:在普通浏览器里跑小程序,是不是自己手写了一套模拟器?

并不是。wxa-runner 的核心原则是:复用官方实现,只做浏览器宿主层。

  1. 官方基础库驱动 :逻辑层直接加载官方的 WAService.js,渲染层加载 WAWebview.js。小程序的 App、Page、Component、exparser 虚拟 DOM、setData 批处理与组件生命周期,全部由微信官方基础库执行;
  2. 官方二进制编译器 :调用微信官方的 wcc 与 wcsc 命令行工具编译 WXML / WXSS,编译逻辑与开发者工具一致;
  3. 平台对齐 :渲染层向基础库声明平台为 devtools,基础库会自动启用微信为其官方工具准备的 Web 端实现;
  4. 宿主层补齐 :wxa-runner 负责实现浏览器里的原生支撑:同源 iframe 消息桥(WeixinJSBridge)、页面栈管理、外壳 UI(模拟导航栏、tabBar、picker、toast 等)以及常用的客户端原生 API。

简单来说:运行时所跑的渲染和业务逻辑,与开发者工具内部是一致的,只是宿主从微信工具换成了普通的 Chrome / Headless 浏览器。


三、 它带来了哪些切实的体验提升?

1. 原生支持 Linux CI,结合 Playwright 跑端到端测试

这是 wxa-runner 最主要的设计目标。

项目封装了官方测试套件 @open-wxa/wxa-runner-playwright。在没有安装开发者工具的 Linux 服务器或 Docker 容器中,它会自动使用内置解包好的基础库和编译器运行测试。

ts 复制代码
// tests/e2e/order.spec.ts
import { test, expect } from '@open-wxa/wxa-runner-playwright';

test('下单流程测试', async ({ miniProgram }) => {
  // 1. 声明式 Mock 原生接口与网络请求(在 App.onLaunch 前就生效)
  await miniProgram.mock('login', { code: 'ci_mock_code' });
  await miniProgram.route('POST **/api/orders', {
    status: 201,
    body: { id: 'order_1001' }
  });

  // 2. 直接打开指定页面
  await miniProgram.open('pages/cart/cart');

  // 3. 使用标准 Playwright 选择器操作页面元素
  await miniProgram.current.locator('#checkout-btn').click();

  // 4. 断言路由自动重试(避免手动 waitFor)与页面 data
  await expect(miniProgram).toHavePath('pages/result/result');
  await expect(miniProgram).toHavePageData('orderId', 'order_1001');

  // 5. 校验发出的实际请求体
  const req = await miniProgram.waitForRequest('**/api/orders');
  expect(req.json()).toMatchObject({ amount: 99 });
});

在 CI 流水线中,直接执行 npx playwright test 即可无头并发运行,彻底摆脱了对桌面端测试机和特殊环境的依赖。


2. 热更新更平滑,整页重载自动恢复页面栈

平时写页面时,经常遇到修改一行代码整个模拟器被重置的情况。wxa-runner 对文件变更进行了细分处理:

  • 样式与模板修改 :
    • 修改 .wxss 时原地替换,不破坏任何页面数据,处于栈底非激活状态的页面也会静默更新;
    • 修改 .wxml 时调用基础库的更新接口,用当前保留的 data 原地重新渲染。
  • 页面代码修改 :
    • Page() 定义的代码,修改后只热替换实例上的函数,已输入的表单与数据不变;
  • 整页重载时保留页面栈 :
    • 当改动了 app.js 或公共模块导致必须整体刷新时,宿主会在刷新前自动记录当前页面栈与各层级参数;
    • 刷新就绪后,按顺序重新压栈打开原页面,无需开发者手动一层层重新点进去。

3. 调试聚焦业务代码,自动过滤官方底层脚本

在 Chrome DevTools 里调试时,最怕单步调试(Step Into)掉进官方混淆源码。

wxa-runner 在服务层处理 Source Map 时,自动将基础库脚本(WAService.js 等)加入了 DevTools 的 Ignore List(忽略列表):

  • 控制台的报错堆栈、日志来源链接,均直接锚定在业务代码或具体页面组件上;
  • 业务 JS 均为独立模块注入,行号与源文件一致;
  • 发生编译错误时,直接显示官方编译器返回的文件路径与行列号,修复后自动恢复。

4. 非侵入的 setup 机制与可选的真实登录

在本地联调时,经常需要特定的登录态或地理位置。

通过项目根目录的 wxa.setup.ts,可以在业务代码执行之前完成环境预置,不用在业务代码里硬塞任何测试判断:

ts 复制代码
// wxa.setup.ts
import { defineSetup } from '@open-wxa/wxa-runner-core/setup';

export default defineSetup({
  // 启动即有 Token
  storage: {
    token: 'mock-user-token',
    userInfo: { nickName: '测试账号' }
  },
  // 模拟位置
  mock: {
    getLocation: { latitude: 39.9042, longitude: 116.4074 }
  },
  // 拦截后端接口返回假数据
  network: {
    routes: [
      {
        url: '**/api/user/info',
        response: { role: 'vip' }
      }
    ]
  }
});

如果后端接口强制校验真实微信 session,可以在启动时增加 --real-login 参数:

bash 复制代码
npx wxa-runner dev ./dist/dev/mp-weixin --real-login

它会借用本机正在运行的微信开发者工具的登录票据(Ticket),直接换取真实的 wx.login code 和手机号,方便与真实后端联调。


5. 一些注重保真度的底层工程设计

  • 同源 iframe 同步桥 :逻辑层与渲染层都在同源 iframe 下,直接通过内存引用调用宿主方法,绕过 HTML5 postMessage 。使得 wx.getStorageSync、wx.getSystemInfoSync 等方法可以严格同步返回,符合小程序规范;
  • Canvas 2D 层级修正 :微信开发者工具的 Canvas 2D 是原生组件,常常盖在所有 DOM 之上导致遮挡弹层。wxa-runner 在页面初始化时,把逻辑层创建的 Canvas DOM 节点物理搬迁进页面的渲染层 DOM 结构中,使其遵循正常的 CSS 层叠上下文(z-index),还原真机上的视觉层级;
  • 手势防抖处理:桌面端鼠标模拟触摸容易引发微小抖动,导致 Swiper 等横向滑动组件被误识别为纵向滑动。运行时专门对按下后 8px 内的移动做了滤波防抖,避免手势被误吃。

四、 坦诚说明:它的边界与已知限制(避免踩坑)

每个工具都有其特定的设计边界。wxa-runner 是为了补足开发效率和 CI 测试而生,并不是要全盘替代官方工具。在考虑接入前,请务必了解以下客观限制:

  1. 不替代微信开发者工具的核心发布功能 :
    • 代码上传、真机预览、版本管理、真机远程调试,仍然必须使用微信开发者工具;
  2. 基础库版本范围 :
    • 默认采用并深度验证过的版本为 ≤ 3.7.0 (3.7.1 和 3.8.x 虽已跑通测试,但需手动通过 --baselib 指定);
  3. 部分特定能力暂不支持 :
    • 不支持小程序插件、Worker 线程;
    • 不支持蓝牙(Bluetooth)、NFC、Wi-Fi 等强依赖手机硬件的特定 API;
    • 对于尚未实现的原生 API,宿主默认返回成功并在控制台发出提醒(测试模式下会记录到错误列表中);
  4. 真机表现始终以真机为准 :
    • 导航栏、系统弹窗等外壳 UI 是在浏览器中模拟的,涉及复杂机型适配或发布前最后验收,务必在真机上进行回归。

五、 与官方 automator 的选型建议

对比维度 微信官方 miniprogram-automator wxa-runner
运行宿主 微信开发者工具(需桌面 GUI)或真机 普通 Chrome(可 Headless 无头运行)
操作系统支持 仅 Windows / macOS Linux / Windows / macOS
CI 自动化门槛 较高(需自建或采购带桌面环境的测试机) 极低(可在 Linux Docker/流水线中直接跑)
测试框架生态 使用 automator 专有 API 深度结合 Playwright 生态(Trace/自动重试/并发)
真机自动化 支持(remote() 控制真机) 不支持(只跑在浏览器)
小程序插件支持 支持 暂不支持
本地调试体验 较重,刷新重置页面栈 轻量快捷,保留状态与页面栈,过滤底层栈

推荐的使用姿势:

  • 日常需求开发与联调 :用 wxa-runner 体验轻快的 Chrome 调试与页面栈恢复;
  • Git 提交触发的自动化流水线 :在 Linux CI 里用 wxa-runner + Playwright 自动跑全量用例,拦截业务主流程 bug;
  • 发版前验收:在微信开发者工具中进行代码上传,并在真机上进行最终确认。

六、 快速上手

1. 本地启动

bash 复制代码
# 原生小程序
npx wxa-runner dev .

# uni-app (Vue 3)
pnpm build:mp-weixin --watch
npx wxa-runner dev ./dist/dev/mp-weixin

启动后控制台会输出访问地址(默认 http://localhost:3000),浏览器直接打开即可。

2. 编写第一个 E2E 测试

安装依赖:

bash 复制代码
pnpm add -D @playwright/test @open-wxa/wxa-runner-playwright

创建 playwright.config.ts:

ts 复制代码
import { defineConfig } from '@playwright/test';
import type { WxaOptions } from '@open-wxa/wxa-runner-playwright';

export default defineConfig<WxaOptions>({
  testDir: './e2e',
  use: {
    channel: 'chrome',
    // uni-app 或 Taro 项目配置产物目录
    wxaProject: './dist/dev/mp-weixin',
  }
});

运行用例:

bash 复制代码
npx playwright test

七、 结语

wxa-runner 并不是一个想要推倒重来、彻底取代官方工具的"颠覆式"项目,而是一次针对小程序开发调试效率与 Linux CI 落地难题的工程务实尝试。

它通过复用官方核心基础库,确保了运行时的真实性;同时借助浏览器的开放性,打通了现代前端测试与调试生态。目前项目已经在真实的 Vue 3 uni-app 与原生业务中平稳运行,代码完全开源(遵循 MIT 协议)。

如果你的团队也在苦于 Linux CI 无法跑小程序测试,或者希望日常本地调试更轻快一些,欢迎体验并提出宝贵的 Issue 和 PR!

相关推荐
德林恩宝1 小时前
搜索 Agent 的两条路——训练一个,还是工程搭一个
人工智能·大模型·llm·agent
vivo互联网技术2 小时前
域名埋点零侵入实时校验实践:基于代理转发与规则驱动的工具设计
前端·测试·监控·代理·埋点
进击的雷神2 小时前
怕 AI Agent 乱删文件?Tank-OS 把它锁进不可变系统
人工智能·ai·api·agent
FITA阿泽要努力3 小时前
/goal 命令:给 Agent 设定目标
服务器·人工智能·python·agent
Json____3 小时前
用 Node.js + Vue3 打造一个家居装修 AI 智能咨询助手
node.js·agent·网站开发·智能体·wwwoop.com
一缕82年的清风3 小时前
当成千上万个Agent涌入系统:高并发大模型网关怎么搭?
agent
周永恒3 小时前
CODESYS AI实践 - 单元测试
ai·开源·测试·plc·codesys