小程序自动化测试与 CI/CD 实战:miniprogram-simulate、automator 与流水线搭建

小程序自动化测试与 CI/CD 实战:miniprogram-simulate、automator 与流水线搭建

admin
2026-06-23 / 0 评论 / 203 阅读

小程序自动化测试与 CI/CD 实战:miniprogram-simulate、automator 与流水线搭建

"每次发版前手工点一遍所有页面"——小程序项目超过 20 个页面后,人工回归就是一场酷刑,而且一定漏测。这篇介绍微信官方的两大测试利器(miniprogram-simulate 组件测试、miniprogram-automator 端到端测试)和基于 miniprogram-ci 的自动化发布流水线,把发版从"手工活"变成"流水线作业"。

一、测试金字塔:小程序版

        /  E2E 测试  \        automator(真机/工具自动化)
       / 业务流程回归  \       核心链路:登录、下单、支付
      /---------------\
     /   页面测试      \      Page 级别,模拟交互
    /-------------------\
   /    组件测试          \   miniprogram-simulate(单组件渲染+断言)
  /-----------------------\
 /  纯函数/工具类单元测试    \  Jest(无渲染依赖的逻辑)
/---------------------------\

比例原则:单元测试多而快,E2E 测试少而稳。全用 E2E 会又慢又脆,全用单元测试覆盖不了真实渲染。

二、组件测试:miniprogram-simulate

2.1 安装与配置

npm install --save-dev miniprogram-simulate jest
// package.json
{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "testEnvironment": "jsdom"
  }
}

2.2 测试一个组件

假设有个 price-tag 组件,props 传分转换为元展示:

// test/price-tag.test.js
const simulate = require('miniprogram-simulate')
const path = require('path')

test('price-tag 渲染价格', () => {
  const id = simulate.load(path.join(__dirname, '../components/price-tag/index'), {
    // 处理 usingComponents 里的自定义组件引用
    rootPath: path.join(__dirname, '../')
  })
  const comp = simulate.render(id, {
    price: 9900,          // 分
    currency: '¥'
  })
  const parent = document.createElement('parent-wrapper')
  comp.attach(parent)

  // 断言渲染结果
  expect(comp.querySelector('.price').dom.innerHTML).toContain('99')
  expect(comp.querySelector('.symbol').dom.innerHTML).toBe('¥')

  // 修改数据触发更新
  comp.setData({ price: 1050 })
  expect(comp.dom.innerHTML).toContain('10.5')
})

2.3 测试组件事件

test('点击触发 buy 事件', () => {
  const id = simulate.load('/components/goods-card/index')
  const comp = simulate.render(id, { goods: mockGoods })

  // 监听组件自定义事件
  let received = null
  comp.addEventListener('buy', (e) => { received = e.detail })

  // 模拟点击
  comp.querySelector('.buy-btn').dispatchEvent('tap')

  return simulate.sleep(10).then(() => {
    expect(received).toEqual({ goodsId: 'g1', count: 1 })
  })
})

适用边界:simulate 是在 jsdom 里模拟小程序运行时,wxs、部分原生组件(map/canvas/web-view)渲染不了。这类组件用 automator 的真机/工具链路测。

三、端到端测试:miniprogram-automator

3.1 启动与连接

npm install --save-dev miniprogram-automator
// test/e2e/login.e2e.js
const automator = require('miniprogram-automator')

describe('登录流程', () => {
  let miniProgram, page

  beforeAll(async () => {
    miniProgram = await automator.launch({
      cliPath: 'D:/software/wechat-web-devtools/cli.bat',  // 开发者工具 CLI 路径
      projectPath: 'D:/work/my-miniprogram',               // 项目路径
      // 需要开发者工具开启「服务端口」(设置→安全)
    })
    await miniProgram.reLaunch('/pages/login/login')
    page = await miniProgram.currentPage()
  }, 60000)

  afterAll(async () => {
    await miniProgram.close()
  })
})

3.2 页面元素操作与断言

test('微信授权登录成功后跳转首页', async () => {
  // 获取元素
  const btn = await page.$('.login-btn')
  expect(await btn.text()).toBe('微信一键登录')

  // mock 授权弹窗(真机上无法自动点授权,mock wx API 是 E2E 的常规手段)
  await miniProgram.mockWxMethod('getSetting', {
    authSetting: { 'scope.userInfo': true }
  })
  await miniProgram.mockWxMethod('login', { code: 'mock-code' })

  // 点击
  await btn.tap()

  // 等待页面跳转(轮询获取当前页面路径)
  await miniProgram.pageWaitFor('/pages/index/index')
  const newPage = await miniProgram.currentPage()
  expect(newPage.path).toBe('pages/index/index')

  // 断言页面数据
  const data = await newPage.data()
  expect(data.userInfo.nickName).toBeTruthy()
})

3.3 mock 能力是 E2E 的灵魂

真机自动化测不了微信授权弹窗、支付密码输入这类系统级交互,mockWxMethod 是官方给的口子:

// mock 掉支付,专注测试支付成功后的业务流转
await miniProgram.mockWxMethod('requestPayment', { errMsg: 'requestPayment:ok' })

// mock 网络请求,构造稳定测试环境
await miniProgram.mockWxMethod('request', { data: { code: 0, data: mockOrder } })
// ...测试结束后恢复
await miniProgram.restoreWxMethod('request')

策略:mock 边界 API(登录/支付/授权),不 mock 业务接口——业务接口走测试环境真实数据,E2E 才有回归价值。

3.4 截图对比(可选)

// 关键页面截图,接入像素对比工具(如 pixelmatch)做视觉回归
await page.screenshot({ path: `snapshots/${page.path.replace(/\//g, '_')}.png` })

四、发布流水线:miniprogram-ci

手动点"上传"发版的问题:谁传的、什么时候传的、传的什么代码全靠自觉。ci 工具让发版代码化。

4.1 准备密钥

小程序后台 → 开发管理 → 开发设置 → 小程序代码上传,生成上传密钥(IP 白名单建议配置)。

npm install --save-dev miniprogram-ci

4.2 上传脚本

// scripts/upload.js
const ci = require('miniprogram-ci')
const path = require('path')

async function upload({ version, desc }) {
  const project = new ci.Project({
    appid: process.env.WX_APPID,
    type: 'miniProgram',
    projectPath: path.resolve(__dirname, '../dist'),
    privateKeyPath: path.resolve(__dirname, './private.key'),
    ignores: ['node_modules/**/*']
  })

  const result = await ci.upload({
    project,
    version,                        // 版本号,如 1.4.2
    desc,                           // 版本描述
    setting: {
      es6: true,
      minify: true,                 // 压缩
      autoPrefixWXSS: true
    },
    // 机器人编号 1-30:不同流水线用不同机器人,后台显示区分来源
    robot: process.env.CI_ROBOT || 1
  })
  console.log('上传成功', result)
}

upload({ version: process.env.VERSION, desc: process.env.DESC })

4.3 预览二维码(测试分发)

const qrcode = await ci.preview({
  project,
  desc: '提测版本',
  qrcodeFormat: 'image',
  qrcodeOutputDest: './preview.jpg'
})
// 把 preview.jpg 推到测试群,测试同学扫码即测

4.4 完整流水线(GitHub Actions 示例)

# .github/workflows/release.yml
name: miniprogram-release
on:
  push:
    tags: ['v*']

jobs:
  test-then-upload:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }

      - name: 安装依赖
        run: npm ci

      - name: 单元测试 + 组件测试
        run: npm test -- --coverage

      - name: 构建
        run: npm run build

      - name: 上传体验版
        env:
          PRIVATE_KEY: ${{ secrets.WX_PRIVATE_KEY }}
          VERSION: ${{ github.ref_name }}
        run: |
          echo "$PRIVATE_KEY" > scripts/private.key
          node scripts/upload.js
        # 私钥用 GitHub Secrets 存,永远不进仓库

      - name: 通知测试群
        run: curl -X POST ${{ secrets.WEBHOOK }} -d '{"msg":"体验版已更新: ${{ github.ref_name }}"}'

打 tag 触发 → 跑测试 → 构建产物 → 上传体验版 → 群通知领测。全流程 5 分钟,人工介入为零,正式版发布只是最后一步"提交审核"由人在后台点一下确认。

五、测试策略落地建议

项目阶段投入策略
0-5 个页面的新项目只测纯函数(Jest),先把工具函数测稳
10+ 页面,开始多人协作引入组件测试,公共组件(请求弹层/卡片)全覆盖
20+ 页面,有交易链路E2E 覆盖核心链路(登录/下单/支付),流水线强制卡点
多端多项目沉淀测试脚手架,统一 mock 数据管理

别追求覆盖率数字:90% 覆盖率但全是无断言的快照测试,不如 40% 覆盖率但每条都测在交易关键路径上。

六、避坑清单

现象解法
automator 连不上工具launch 超时开发者工具开启服务端口,关多个实例
E2E 时好时坏等待缺失用 pageWaitFor 轮询代替 sleep
真机授权弹窗卡死自动化无法点击mockWxMethod 跳过系统弹窗
simulate 渲染不出组件使用了 wxs/原生组件该组件改用 automator 测
上传报 40125密钥或 IP 白名单问题检查 privateKeyPath 与后台 IP 白名单
流水线密钥泄漏私钥提交到仓库用 CI 平台 Secrets,脚本里生成
robot 冲突多流水线互相顶掉不同环境配不同 robot 编号

写在最后

小程序工程化的终局形态:git push 打 tag → 测试自动跑 → 体验版自动传 → 二维码自动进群 → 人只做两件事:看测试报告,点提交审核。前期搭流水线要花两三天,但从第一次"发版救火"开始,这些投入就开始指数级回本。测试代码不是负担,是团队交付速度的复利资产。

0

评论 (0)

取消
0:00