- 移动开发
- 跨平台
- 前端
- UI组件
- OpenHarmony
【免费下载链接】weex
A framework for building Mobile cross-platform UI
Weex 是一套用于构建移动端跨平台 UI 的框架,同一份 Vue/Weex 页面代码可运行在 Android、iOS 与 H5 三种端上。为了让这套"一次编写、三端运行"的代码在每次改动后依然行为一致,仓库在 test/ 目录下沉淀了一套完整的端到端(E2E)自动化测试体系:以 Macaca 作为移动端驱动引擎、Mocha 作为用例框架、Blink-Diff 做像素级截图比对,覆盖内置组件与模块的全部常用功能。本文将以 test/README.md 为核心线索,结合仓库中的运行脚本与真实用例,完整讲解该测试体系的环境搭建、运行方式、目录结构与用例编写方法,让读者既能一键跑通全量回归,也能自行扩展新的测试页面与断言用例。
测试体系概览
按照 test/README.md 的定位,这套测试的目标是:
覆盖所有内置公共组件(components)与模块(modules)的功能,包括真实应用中的常见 UI 模式,例如基于列表的页面(list-based page)、包含各种输入控件的表单(a form with all kind of input)等。
也就是说,它并非单元测试(单元测试在 test/js-framework 中),而是面向真实运行环境的端到端测试:先把每个用例对应的 Weex 页面编译成 JS bundle,再通过 Macaca 驱动 Android 模拟器 / iOS 模拟器 / Chrome 浏览器加载页面,模拟真实用户点击、输入、滑动,最后通过 DOM 断言或截图比对来验证三端行为是否一致。
整套测试链路可以概括为三步:
- 构建:把 test/pages 下的 Vue 页面编译为可被三端加载的 JS bundle;
- 服务:在本地启动静态资源服务器,供页面与 bundle 下载;
- 驱动:通过 Macaca 启动目标端的 App/浏览器,由 Mocha 执行 test/scripts 下的用例脚本,逐步操作页面并断言结果。
环境准备(Setup)
官方 README 的第一步就是安装 Macaca 环境。Macaca 是本套测试的移动端驱动核心,它负责把 WebDriver 协议翻译成对 Android/iOS 模拟器的真实控制指令。环境搭建包含以下要点:
- 安装 Macaca 及对应平台的驱动(iOS 需要 Xcode 与模拟器运行时,Android 需要 SDK、Platform Tools 与 AVD 模拟器镜像);
- 准备 Node.js 运行时(仓库 package.json 声明
engines.node >= 8); - 若在 CI 中运行,还需要安装 Chrome 用于 H5 端测试。
仓库提供了 test/ci-funcs.sh 作为 CI 环境自动准备的参考脚本,其中封装了四个关键函数:
installAndroidSDK:通过android update sdk安装 platform-tools、build-tools-23.0.2、android-19/23 平台与 armeabi-v7a 系统镜像;createAVD/startAVD:创建并启动名为weexavd的 Android 模拟器(-no-audio -no-window无头模式,适合 CI);waitForEmulator:轮询adb -e shell getprop init.svc.bootanim直到模拟器完成启动,再发送keyevent 82解锁;installNode:通过 nvm 安装 Node 7.0.0(与engines.node >= 8相比略旧,说明该脚本面向当时的 CI 基线)。
这些函数说明:要在 CI 上跑通 Android 端测试,至少需要 SDK 工具链、一个可用的 AVD 以及等待模拟器就绪的同步逻辑;本地开发时可以直接用 Android Studio 自带模拟器,不必完全照搬该脚本。
运行内置测试用例(Run)
四步运行流程
test/README.md 给出了运行内置用例的标准四步流程,这是整个文档最核心的实战内容:
- 进入 Weex 项目根目录;
- 执行
npm install安装全部依赖(Mocha、weex-wd、macaca-utils、blink-diff、serve 等测试相关依赖都在 package.json 的devDependencies中); - 执行
./test/serve.sh构建测试 bundle 并启动静态资源服务; - 执行
./test/run.sh [platform]执行 Weex SDK 测试,platform可选all(默认)、android、ios、h5。
构建与静态服务:serve.sh
test/serve.sh 的实际内容非常简短,但每行都对应一个重要环节:
#!/usr/bin/env bash npm run build:vue # 构建 Vue 相关的 JS framework bundle npm run build:ci & # 后台构建 CI 测试 bundle port="${serport:-12581}" # 端口可用环境变量 serport 覆盖,默认 12581 npm run serve:no-port -- -p $port # 用 serve 把仓库根目录作为静态站点几点值得注意:
- 端口通过环境变量
serport控制,默认12581。这个端口会贯穿测试全过程:test/scripts/util.js 中同样读取process.env.serport || 12581来拼接页面 URL; npm run serve:no-port对应 package.json 中的"serve:no-port": "serve ./",即把仓库根目录作为静态资源根路径,这样测试页面可以直接通过http://<ip>:12581/test/build/...访问到编译产物;build:ci会调用 [test/scripts/build.js 对应的构建流程](见 scripts/config.js),把 test/pages 下的 Vue 页面编译为 JS bundle 输出到test/build目录。
三端测试驱动:run.sh
test/run.sh 是测试执行的总调度脚本,它按平台拆分为三条执行路径,并在结束时统一清理 Macaca 服务进程(killserver)。脚本整体流程如下:
platform=${1:-android} # 第一个参数:平台,默认 android needCoverage=${2:-noCover} # 第二个参数:是否需要覆盖率,默认 noCover killserver # 先杀掉残留的 macaca-cli-server # android 分支 runAndroid ./test/scripts/ "$needCoverage" # web 分支(对应 README 中的 h5) runWeb ./test/scripts/ # 其余情况走 iOS runiOS ./test/scripts/ "$needCoverage" killserver各平台分支的核心逻辑如下表(来源于 test/run.sh 源码):
| 平台 | 构建步骤 | 测试命令 |
|---|---|---|
| Android | ./gradlew clean assembleDebug构建 playground 调试包(传入cover时切换为带 JaCoCo 覆盖率插桩的:weex_sdk:assembleDebug) | platform=android mocha test/scripts --reporter mocha-simple-html-reporter -f '@ignore-android' -i --recursive --bail --retries 3 |
| iOS | pod update+ 三组xcodebuild分别构建 Pods-WeexDemo、WeexSDK 与 WeexDemo(iphonesimulator,关闭代码签名),并killAll Simulator清理 | platform=ios mocha ... -f '@ignore-ios' -i ... |
| Web/H5 | 无需构建原生包 | browser=chrome mocha ... -f '@ignore-web' -i ... |
脚本中的startMacacaServer会后台拉起macaca server --verbose,并通过nc -z 127.0.0.1 3456轮询等待 Macaca 默认端口 3456 就绪;startWeexServer则等待 12581 静态服务可用。也就是说,一次测试运行时实际存在三个常驻进程:Macaca 服务(3456)、静态资源服务(12581)、Mocha 测试进程。
关于 Mocha 命令需要说明两点:
-f '@ignore-android' -i表示"忽略标题中包含 @ignore-android 的用例",即允许单个用例通过命名声明自己跳过某个平台,实现三端用例共享一套脚本;--retries 3允许用例失败自动重试 3 次,可有效缓解移动端测试常见的偶发不稳定问题;- 结果统一输出为
report.html(mocha-simple-html-reporter),便于在 CI 上归档与查看。
平台参数的实际行为
README 声称platform可为all(默认)、android、ios、h5。对照 test/run.sh 源码可以发现实际映射略有不同:脚本只判断了android与web两个分支,h5实际对应web分支(在 Chrome 中运行),而all并没有独立的并发分支。换句话说,目前一次只能运行单一平台,"all" 更接近文档层面的约定而非脚本行为;README 标注为 "**todo##" 的独立项目测试(bash run.sh [platform] [path])也属于规划中的能力——当前run.sh的第二个参数实际被解释为覆盖率开关(cover/noCover),并不会按路径加载外部项目。从源码结构推断,后续若支持path参数,需要让run.sh把用例目录从固定的./test/scripts/改为参数指定的目录。
测试目录结构解析
test/README.md 的 Develop 章节只列出了两个目录,但实际 test 目录下共有 8 个顶层条目,各自职责如下:
| 目录/文件 | 职责 |
|---|---|
| test/scripts | 测试脚本。测试命令会逐个执行该目录下的每个用例文件,是断言与操作逻辑所在 |
| test/pages | Weex 页面脚本。这些 Vue 页面会在运行测试前被编译成 JS bundle,所有测试实际都运行在这些页面上 |
| test/screenshot | 截图比对基准图,如 border 组件的 Android/iOS 对照截图 |
| test/js-framework | JS Framework 层面的用例与测试器(对应 package.json 中的test:case/test:unit脚本) |
| test/serve.sh | 构建并启动静态服务 |
| test/run.sh | 三端测试总调度 |
| test/ci-funcs.sh | CI 环境准备函数库 |
| test/update-cli.sh | 测试 CLI 更新脚本 |
其中scripts与pages一一对应、按主题分子目录组织:components(组件行为)、modules(模块调用)、attributes(属性)、css(样式)。例如:
- 组件目录:test/scripts/components/a-src.test.js ↔ test/pages/components/a-src.vue;
- 模块目录:test/scripts/modules/storage-event.test.js ↔ test/pages/modules/storage-event.vue。
这种"一页一用例"的组织方式让测试意图非常清晰:页面负责提供可交互的 UI 与结果回显,脚本负责像真人一样驱动页面并断言结果。
测试用例编写实战:从页面到断言
理解这套体系最快的方式,是完整拆解一个真实用例。以 storage 模块的storage-event用例为例。
第一步:编写测试页面
test/pages/modules/storage-event.vue 是一个典型的 Weex 测试页面:页面内放置两个按钮setItem、getItem,点击后调用weex.requireModule("storage")的对应方法,并把回调结果写入resultTxt回显到页面上:
<template> <div> <button value="setItem" @click.native="setItem"></button> <button value="getItem" @click.native="getItem"></button> <text>{{resultTxt}}</text> </div> </template> <script> var storage = weex.requireModule("storage"); module.exports = { data: { resultTxt: '' }, methods: { setItem: function () { storage.setItem('key', 'value', () => { this.resultTxt = 'setItem success'; }); }, getItem: function () { storage.getItem('key', (e) => { this.resultTxt = 'getItem:' + e.data; }); } } } </script>页面中的wxc-desc组件专门用于用中文描述"测试点"与"测试方式",相当于把人工回归时的检查清单固化在页面里。
第二步:编写测试脚本
test/scripts/modules/storage-event.test.js 则负责驱动与断言,它的骨架可以拆成五部分:
var assert = require('chai').assert var wd = require('weex-wd') var util = require("../util.js"); var goal = 'storage-event'; var timeout = util.getGETActionWaitTimeMills(); describe('weex ' + goal + ' test', function () { this.timeout(util.getTimeoutMills()); // 1. 放宽 Mocha 超时(CI 下 60 分钟,本地 10 分钟) var driver = util.createDriver(wd); // 2. 创建/复用 WebDriver beforeEach(function () { return util.init(driver) // 3. 初始化驱动(启动 App/浏览器) .get(util.getPage('/modules/' + goal + '.js')) // 4. 加载对应编译产物 .waitForElementByName(goal, timeout, 2000) // 5. 等待页面元素出现 }); // ...用例断言 });用例本身是一条完整的用户路径:
it('#1 ' + goal + ' event', () => { return driver .waitForElementByName('setItem', timeout, 2000).click() .waitForElementByName('setItem success', timeout, 2000) // 断言写入成功 .waitForElementByName('getItem', timeout, 2000).click() .waitForElementByName('getItem:value', timeout, 2000) // 断言读取结果 })整个流程的精髓是把断言对象做成页面上的可见文本:storage.setItem的回调把resultTxt改成setItem success,脚本只需waitForElementByName('setItem success')就能同时完成"操作成功 + UI 正确回显"的双重验证,完全不依赖原生控件的内部状态,天然跨端可用。
第三步:复用测试基础设施
几乎所有用例都通过 test/scripts/util.js 获得统一的设备与手势能力,它是整个测试体系的"基础设施层":
- 平台与设备配置:通过环境变量
platform(默认 android)、browser、serport、run_in_ci决定目标端。Android 加载playground-debug.apk,iOS 加载WeexDemo.app,H5 则直接驱动 Chrome; - URL 拼接(
getPage):Android/iOS 使用wxpage://<ip>:<port>/test/build<name>自定义 scheme 唤起 SDK 加载页面,H5 使用http://<ip>:<port>/vue.html?page=/test/build-web<name>,getDeviceHost会自动探测本机非回环 IPv4 地址; - 手势封装:在 WebDriver 上扩展了
dragUpAndDown、drag(支持 toUp/toLeft/toRight/toDown 四个方向与起点偏移)、swipeLeft/swipeRight、clickScreenById(按元素 id 计算中心点点击)等 Promise 链方法,模拟真实用户的上滑加载、左右滑动等操作; - 截图能力:
saveShot把 Base64 截图写入本地文件; - 生命周期:
init负责初始化驱动并等待 20 秒(注释说明 iOS 无法立即检测到 App),quit在退出前保存最后一张截图到test/last.png并返回上一页,方便失败排查。
组件用例可以进一步体现这些基础设施的配合。以 test/scripts/components/a-src.test.js 为例,它验证<a>组件的 src 跳转与动态更新:先点击a-itself断言跳到a-support-href1页面,再点击changeSrc把href动态改为a-support-href2后再次跳转断言。对应页面 test/pages/components/a-src.vue 中用test-id属性标记关键元素(如a-itself、content-inside-a),脚本据此精准定位,覆盖了"点击组件本身"与"点击组件内部内容"两类真实场景。
截图比对机制:三端视觉一致性验证
除了 DOM 断言,这套体系还支持像素级截图比对,用于验证同一页面在三端上的渲染效果一致。test/scripts/util.js 中的diffImage基于blink-diff实现:
var diff = new BlinkDiff({ imageAPath: imageAPath, // 基准图路径(首次运行时自动写入基准) imageB: imageB, // 当前截图 Buffer thresholdType: BlinkDiff.THRESHOLD_PIXEL, threshold: threshold, // 允许的差异像素阈值 imageOutputPath: outputPath, // 差异图输出路径 cropImageA: isIOS ? {y:128} : {y:242,height:1530}, // 裁剪状态栏/导航栏区域 cropImageB: isIOS ? {y:128} : {y:242,height:1530} });其中的裁剪参数值得一提:iOS 裁剪从 y=128 开始(去掉状态栏与导航栏),Android 从 y=242 开始且固定高度 1530(源码注释解释了 Android 需要减去 status bar 72 + navigator bar 170 的偏移),避免不同系统 UI 栏差异干扰页面内容比对。diff.hasPassed(result.code)判断是否在像素阈值内通过,并输出实际差异像素数。
test/screenshot 目录下的基准图就是这套机制的产物,例如 border 组件在 Android 与 iOS 上的渲染对照:
从border-android.png(1080×1920)与border-ios.png(750×1334)的尺寸差异也能看出,比对前必须经过裁剪与归一化处理,才能在不同分辨率屏幕上进行有意义的像素对比。这种"截图即断言"的方式非常适合验证边框、圆角、间距等纯视觉属性,与 DOM 断言互补。
常见问题与调试建议
- 端口冲突:Macaca 固定使用 3456,静态服务默认 12581。若端口被占用,可通过
serport环境变量更换静态服务端口(serport=13000 ./test/serve.sh),但注意util.js读取的是同一个环境变量,需要同步设置; - 偶发失败:移动端测试不稳定是常态,
run.sh已内置--retries 3;若仍失败,可查看report.html与退出前保存的test/last.png定位是操作失败还是渲染不一致; - 新增用例的步骤:在 test/pages 对应子目录新增
.vue页面(用test-id标注关键元素)→ 在 test/scripts 对应子目录新增同名.test.js(复用util.createDriver与手势方法)→ 若涉及视觉断言,先跑一次生成基准图 → 通过./test/run.sh [platform]验证; - 平台差异标注:若某个交互只在部分平台支持,在用例标题中加入
@ignore-android/@ignore-ios/@ignore-web,run.sh会通过-f ... -i自动跳过。
小结
Weex 仓库的这套 E2E 测试体系给出了一个可复制的跨端 UI 测试范式:用真实页面承载可断言结果,用统一基础设施抹平三端差异,用截图比对兜住视觉回归。理解 test/README.md 描述的"构建(serve.sh)→ 驱动(run.sh)→ 断言(scripts/pages)"三层结构后,开发者既可以在本地一键回归 test/pages 中覆盖的内置组件与模块,也可以参照 test/scripts/util.js 的封装模式,为自己的业务页面快速搭建一套 Android、iOS、H5 三端一致运行的自动化测试,并平滑接入 CI(参考 test/ci-funcs.sh)。
- 移动开发
- 跨平台
- 前端
- UI组件
- OpenHarmony
【免费下载链接】weex
A framework for building Mobile cross-platform UI
相关推荐
uni-app(x) 自动化测试实战指南:基于 uni-automator 与 Jest 的跨端 UI 测试体系
uni app x 自动化测试实战指南:基于 uni automator 与 Jest 的跨端 UI 测试体系 uni app x 提供了一套官方自动化测试能力
示例工程前端移动开发跨平台Spock-example数据库测试:H2数据库集成测试的完整解决方案
Spock example数据库测试:H2数据库集成测试的完整解决方案 Spock example项目是Spock测试框架的官方示例项目,专门展示如何在Java
Comprehensive Rust 课程 Web 端到端测试体系详解:基于 webdriverIO、Mocha 与 xtask 的自动化验证方案
Comprehensive Rust 课程 Web 端到端测试体系详解:基于 webdriverIO、Mocha 与 xtask 的自动化验证方案 Compre
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考