尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

expo-updates 端到端测试指南:用 Maestro 搭建 E2E 测试环境并手动验证 Updates API

expo-updates 端到端测试指南:用 Maestro 搭建 E2E 测试环境并手动验证 Updates API expo-updates 端到端测试指南用 Maestro 搭建 E2E 测试环境并手动验证 Updates API【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo本文是 expo-updates 端到端E2E测试环境的完整搭建指南覆盖仓库 packages/expo-updates/e2e/README.md 定义的两条主流程基于 Maestro 的自动化 E2E 测试enabled 套件覆盖更新下载、校验、回滚、资源恢复等 15 个场景以及用于手工验证 Updates API 与 EAS 更新链路的测试项目。读完本文你将掌握 E2E 测试所需的全部环境变量、脚手架命令、iOS/Android 构建与执行方法并理解测试脚手架、测试更新服务器与 Maestro 场景文件的底层实现可直接在当前仓库中复现整套测试。一、e2e 目录总览测试体系的组成部分在动手之前先理解 packages/expo-updates/e2e 目录的职责划分setup/一系列 TypeScript 脚手架脚本负责生成位于仓库外部的测试工程。其中 create-eas-project.ts 对应 E2E 自动化测试工程create-updates-test.ts 对应 Updates API 手动测试工程另有 dev-client、fingerprint、error-recovery、startup、disabled、bricking-measures-disabled、old-arch、tv、custom-init 等变体脚本公共逻辑集中在 project.ts。fixtures/测试工程所需的模板资产。App.tsx、App-apitest.tsx是注入测试工程的入口组件project_files/下打包了 eas.json、.env、Maestro 场景与更新服务器源码、scripts/下的工具脚本custom_init/提供自定义初始化custom init的原生入口源码。README.md本文所述的官方操作指南。测试工程不会生成在仓库内部而是生成在EXPO_REPO_ROOT之外的WORKING_DIR_ROOT下从而避免污染 Expo 主仓库同时脚手架会以npm packlink:相对路径的方式把当前仓库里最新源码而非 npm 发布的版本链接进测试工程保证测试跑的是仓库当下的实现。二、E2Eenabled自动化测试完整搭建流程这一套测试针对 expo-updates 的 enabled 状态即更新功能开启、通过本地 mock 更新服务器验证行为核心流程为准备环境变量 → 生成 EAS 测试工程 → 生成测试更新 bundle → 构建并运行 Maestro 测试。2.1 第一步编写环境准备脚本创建一个 shell 脚本例如setup-e2e.sh用于设置环境变量并清理上次构建残留内容如下示例路径可按实际修改# The location of your local copy of this repo export EXPO_REPO_ROOT/Users/me/myCode/expo # The name of a directory that the test project can live under export WORKING_DIR_ROOT/Users/me/myCode/e2eworking # Other environment variables needed for the test setup export TEST_PROJECT_ROOT$WORKING_DIR_ROOT/updates-e2e export UPDATES_HOSTlocalhost export UPDATES_PORT4747 export EXPO_PUBLIC_UPDATES_SERVER_PORT4747 # Remove and recreate the working directory before executing the setup rm -rf $WORKING_DIR_ROOT mkdir $WORKING_DIR_ROOT各变量的作用对照 create-eas-project.ts 源码可以确认环境变量含义源码中的消费点EXPO_REPO_ROOT当前仓库的本地路径project.ts 通过repoRoot读取也兼容 CI 中注入的EAS_BUILD_WORKINGDIRWORKING_DIR_ROOT测试工程存放的上级目录workingDir path.resolve(repoRoot, ..)计算出默认父目录TEST_PROJECT_ROOT测试工程根目录脚本中process.env.TEST_PROJECT_ROOT || path.join(workingDir, updates-e2e)UPDATES_HOST/UPDATES_PORT本地测试更新服务器的主机与端口脚本启动时强校验缺失会直接抛错App.tsx中的占位符会被替换为这两个值EXPO_PUBLIC_UPDATES_SERVER_PORT测试更新服务器监听端口被 maestro-test-executor.sh 读取为MAESTRO_UPDATES_SERVER_PORT随后执行source scriptname必须使用source执行因为脚本通过export设置的变量需要在当前 shell 会话中持续生效供后续命令使用。2.2 第二步生成 EAS 测试工程在 Expo 仓库根目录执行./packages/expo-updates/e2e/setup/create-eas-project.ts该脚本内部执行了严格的环境校验create-eas-project.tsif (!repoRoot || !process.env.UPDATES_HOST || !process.env.UPDATES_PORT) { throw new Error(Missing one or more environment variables; see instructions in e2e/README.md); }即缺少EXPO_REPO_ROOT、UPDATES_HOST、UPDATES_PORT任一变量都会立即失败。脚本将固定 runtimeVersion 为1.0.0调用 project.ts 中的initAsync完成以下一系列工作清理旧工程非 CI 环境下若TEST_PROJECT_ROOT已存在则递归删除打包 TS 模板对 templates/expo-template-blank-typescript 执行npm pack生成 tarball创建工程用pnpm create expo-app projectName --yes --no-install --template tarball初始化--no-install延迟安装后面统一处理重写依赖preparePackageJson将仓库内各 Expo 包expo、expo-updates、expo-constants、expo-file-system 等按 peer 依赖分批逐一npm pack并写成link:./dependencies/...形式的相对路径同时写入resolutions确保测试工程使用仓库源码而非 npm 发布版写入 app.json通过transformAppJsonForE2E注入测试配置详见 2.4 节安装依赖pnpm install生成并配置代码签名执行pnpm expo-updates codesigning:generate密钥输出到keys/、证书输出到certs/有效期 1 年、通用名E2E Test App与codesigning:configure最后打包keys.tar避免上传 EAS 时被过滤prebuild打包 templates/expo-template-bare-minimum 模板并用本地 CLIpnpm prebuild --no-install --template ...生成 ios/android 原生工程prebuild 后再恢复expo依赖并重新pnpm installAndroid 附加调整在gradle.properties追加android.enableMinifyInReleaseBuildstruerelease 开启压缩混淆并向proguard-rules.pro追加 expo-updates 运行所需的 keep/dontwarn 规则。2.3 第三步生成测试更新 bundle切换到测试工程目录并执行cd $TEST_PROJECT_ROOT pnpm generate-test-update-bundles该脚本对应 fixtures 中 scripts/generate-test-update-bundles.ts由 project.ts 注入为 package.json 的 script会同时生成 Android 与 iOS 的更新 bundle供本地测试更新服务器分发给客户端。注意该 script 只有在configureE2E模式下才会被注入为真实命令否则只是一个echo 1占位。2.4 测试工程 app.json 的关键配置脚手架会通过transformAppJsonForE2Eproject.ts改写测试工程的 app.json这是理解测试行为的关键{ expo: { name: projectName, runtimeVersion: 1.0.0, plugins: [expo-updates, [config-plugins/detox, { skipProguard: true, subdomains: [10.0.2.2, localhost, UPDATES_HOST] }]], newArchEnabled: true, android: { package: dev.expo.updatese2e }, ios: { bundleIdentifier: dev.expo.updatese2e }, updates: { url: http://UPDATES_HOST:UPDATES_PORT/update, assetPatternsToBeBundled: [includedAssets/*], useNativeDebug: true, requestHeaders: { expo-channel-name: default } }, experiments: { autolinkingModuleResolution: true }, extra: { eas: { projectId: 55685a57-9cf3-442d-9ba8-65c7b39849ef } } } }updates.url指向本地 mock 服务器Android 模拟器访问宿主机时使用10.0.2.2requestHeaders中expo-channel-name: default作为 EAS 更新请求头客户端按此通道拉取更新assetPatternsToBeBundled将includedAssets/*下的资源打进更新包用于多资源更新场景生成的工程同时具备reset-to-embedded、set-to-update-1、set-to-update-2脚本通过 scripts/reset-app.ts 切换 App 入口并触发eas update --branchmain --message...供更新资源清理类测试使用。另外initAsync还会把 e2e 测试专用的原生模块注入到仓库内的expo-updates 包中project.ts将 fixtures/E2ETestModule.swift 复制为packages/expo-updates/ios/EXUpdates/E2ETestModule.swift、将 fixtures/UpdatesE2ETestModule.kt 复制为 Android 对应路径并临时把E2ETestModule/UpdatesE2ETestModule追加进packages/expo-updates/expo-module.config.json。这些模块向 JS 层暴露状态查询与操作接口如更新状态机、下载进度、请求头等是 Maestro 断言的基础。由于原生构建在注入之后进行仓库内这些注入在构建完成前不能被清理源码注释也明确说明了这一点因此不要在测试运行中途手动恢复这些文件。2.5 运行 iOS 测试前置条件已启动一个 iOS 模拟器且没有 Android 模拟器在运行。然后依次执行npx pod-install pnpm maestro:ios:debug:build ./maestro/maestro-test-executor.sh ./maestro/tests/updates-e2e-enabled.yml ios debug其中maestro:ios:debug:build实际执行的是project.ts 中注入的 scriptset -o pipefail xcodebuild -workspace ios/updatese2e.xcworkspace -scheme updatese2e \ -configuration Debug -sdk iphonesimulator -arch arm64 -derivedDataPath ios/build | pnpm excprettymaestro-test-executor.sh位于生成的测试工程maestro/目录下fixtures 中的原始文件见 maestro-test-executor.sh它接收三个参数test_suite_path ios|android debug|release。2.6 运行 Android 测试前置条件已启动一个 Android 模拟器且没有 iOS 模拟器在运行。然后执行pnpm maestro:android:debug:build ./maestro/maestro-test-executor.sh ./maestro/tests/updates-e2e-enabled.yml android debug对应构建命令为project.tscd android; ./gradlew :app:assembleDebug; cd ..测试执行器在 Android 上会额外执行adb reverse tcp:port tcp:port把模拟器端口反向映射到宿主机同时映射 8081Metro端口保证模拟器内的客户端能访问宿主机上的更新服务器。2.7 可选单独运行测试更新服务器iOS 或 Android 测试都允许在另一个终端窗口先单独启动测试更新服务器便于调试时观察请求日志./maestro/updates-server/start.ts服务器基于 Express 实现见 server.ts默认监听EXPO_PUBLIC_UPDATES_SERVER_PORT指定的端口默认 4747。它支持通过内建接口精确控制应答内容supportedManifestRequests列出的场景包括no-update-available无更新可用test-update-basic基本更新test-update-invalid-hash/test-update-with-invalid-asset-hash校验失败类场景test-update-with-multiple-assets多资源更新test-update-with-older-commit-time旧提交时间不生效更新test-update-before-rollback/test-rollback回滚相关test-update-for-fingerprint、test-update-for-asset-deletion、test-update-crashing等。服务器同时支持协议版本切换默认 protocol 1、人为延迟artificialDelay模拟慢网络、以及/static路径提供更新资源文件服务。执行器脚本 maestro-test-executor.sh 默认也会在beforeAll阶段自动检测端口并拉起服务器若未占用所以单独启动服务器仅用于调试。三、Maestro 测试执行器与场景文件的工作方式3.1 执行器的生命周期maestro-test-executor.sh 的工作流程为从.env读取配置导出MAESTRO_TEST_SUITE、MAESTRO_PLATFORM、MAESTRO_CONFIGURATION和MAESTRO_UPDATES_SERVER_PORTbeforeAll校验端口变量非空若更新服务器未启动则后台拉起Android 上执行adb reverse端口映射执行maestro -p $MAESTRO_PLATFORM test $MAESTRO_TEST_SUITE——-p指定平台避免 Maestro 在存在多设备时跑错目标无论成败trap cleanup EXIT都会兜底清理Android 失败时从 logcat 中提取崩溃日志FATAL EXCEPTION/AndroidRuntime并输出应用日志最后 300 行过滤掉 Maestro 自身的视图层级刷屏日志随后关闭更新服务器并执行pnpm maestro:platform:uninstall卸载测试应用iOS 为xcrun simctl uninstall booted dev.expo.updatese2eAndroid 为adb uninstall dev.expo.updatese2e。3.2 主测试场景15 个 flow 的组合updates-e2e-enabled.yml 是 enabled 套件的入口它按顺序串联了basic_startAndStop、basic_checkRequestHeaders、basic_reload、reload_update、basic_runUpdate、basic_updateInvalidHash、basic_updateInvalidAssetHash、basic_updateMultipleAssets、basic_updateReusesEmbeddedAssets、basic_updateOldCommitTime、basic_rollback、jsapi_runUpdate、jsapi_stateMachine、jsapi_setRequestHeadersOverride、assetRecovery_restoreAssetFiles共 15 个 flow覆盖更新生命周期、请求头校验、哈希校验失败、多资源、内嵌资源复用、回滚、JS API 与状态机、资源恢复等行为。以 basic_runUpdate.yml 为例可以直观看到下载并运行更新的验证模式通过evalScript调用output.api.serveManifest(test-update-basic, MAESTRO_PLATFORM)指示更新服务器准备一个基本更新launchApp启动应用断言界面上的updateString文本为test即内嵌 bundle 的内容延迟 3 秒后断言state.downloadProgress为1验证启动期间后台完成了下载stopApp后再次launchApp断言updateString变为test-update-1——证明重新启动后加载的是已下载的新 bundle。这些断言依赖 e2e 注入的原生测试模块E2ETestModule.swift、UpdatesE2ETestModule.kt向 UI 暴露的updateString、state.*等测试 ID 与状态。四、Updates API 手动测试项目不写代码验证更新链路如果你不想跑完整的 Maestro 套件而希望在真实 EAS 环境下手动点按式验证 Updates API可以搭建 README.md 第二部分描述的测试项目。它生成的工程通过expo-channel-namemain作为 EAS 更新请求头README 描述实际写入 app.json 的默认值为default见 create-updates-test.ts可自行调整。4.1 环境变量# The location of your local copy of this repo export EXPO_REPO_ROOT/Users/me/myCode/expo # The name of a directory that the test project can live under export WORKING_DIR_ROOT/Users/me/myCode/e2eworking # The user name of the Expo account you are logged into export EXPO_ACCOUNT_NAMEmyexpoaccount # Other environment variables needed for the test setup export TEST_PROJECT_ROOT$WORKING_DIR_ROOT/MyUpdatesApp export EX_UPDATES_NATIVE_DEBUG1 # Remove and recreate the working directory before executing the setup rm -rf $WORKING_DIR_ROOT mkdir $WORKING_DIR_ROOT其中EXPO_ACCOUNT_NAME来自 project.ts 的定义process.env.EXPO_ACCOUNT_NAME || myusername它会被用于拼装 Android 包名com.account.projectName与 iOS bundleIdentifier见 create-updates-test.ts。EX_UPDATES_NATIVE_DEBUG1用于开启原生侧调试能力。4.2 生成工程并配置 EAS./packages/expo-updates/e2e/setup/create-updates-test.ts cd $TEST_PROJECT_ROOT eas init eas update:configure与 E2E 工程不同该脚本通过transformAppJsoncreate-updates-test.ts将assetPatternsToBeBundled设为assetsInUpdates/*对应 fixtures 中 project_files/assetsInUpdates 目录并把测试工程的 App-apitest.tsx 复制为App.tsx同时关闭 Android 上的 JS 调试将BuildConfig.DEBUG替换为false见 project.ts。setupManualTestAppAsync还会复制embeddedAssets等 fixtures。4.3 本地构建并运行npx pod-install # if testing iOS npx expo run:ios|android4.4 手动验证更新创建更新执行eas update选择默认分支main和默认提交信息客户端检测更新重启客户端或点击界面上的 Check for update manually 按钮若存在更新界面会出现 Download and run update 按钮点击后客户端将下载并加载、启动该更新。五、更多 E2E 变体与 CI 支持除本文主流程外setup/ 还提供了多组面向特定场景的工程生成脚本对应不同的 app.json 变换与 fixture 组合均可在 project.ts 中找到对应的 transform 函数与 setup 函数create-dev-client-eas-project.ts使用 expo-dev-client变换中删除useNativeDebug用于 dev-client 场景场景见 updates-e2e-dev-client.ymlcreate-fingerprint-eas-project.tsruntimeVersion 策略切换为{ policy: fingerprint }并附带.fingerprintignorefixturecreate-disabled-eas-project.tsupdates.enabled: false验证关闭更新的行为updates-e2e-disabled.ymlcreate-error-recovery-eas-project.ts注入fallbackToCacheTimeout: 5000验证错误恢复updates-e2e-error-recovery.ymlcreate-startup-eas-project.ts启动流程相关updates-e2e-startup.ymlcreate-bricking-measures-disabled-eas-project.ts设置disableAntiBrickingMeasures: truecreate-eas-project-old-arch.tsnewArchEnabled: false的旧架构验证create-eas-project-tv.ts面向 tvOS额外注入react-native-tvos依赖与react-native-tvos/config-tv插件、修改 Podfile 中的 OOT 配置ios→tvos并提供tvos:build脚本create-eas-project-custom-init.ts从 fixtures/custom_init 覆盖AppDelegate.swift、SceneDelegate.swift、MainApplication.kt、MainActivity.kt并在 Androidgradle.properties写入EX_UPDATES_CUSTOM_INITtrue、iOSPodfile.properties.json写入updatesCustomInittrue验证自定义初始化路径。从 project.ts 注入的脚本可以看出工程还内置了 CI 相关钩子eas-build-pre-install对应eas-hooks/eas-build-pre-install.sh、eas-build-on-success以及check-android-emulatorscripts/check-android-emulator.ts等辅助脚本说明整套体系可同时支撑本地调试与云端/CI 构建执行。六、常见问题与排查建议以下建议均从脚本实现中可以推断得出Missing one or more environment variablescreate-eas-project.ts启动时强校验EXPO_REPO_ROOT或EAS_BUILD_WORKINGDIR、UPDATES_HOST、UPDATES_PORT请确认环境准备脚本已source且变量拼写正确Maestro 找不到元素 / 应用启动即崩溃执行器在 Android 失败时会自动 dump logcat 中的崩溃信息与应用日志先查看这段输出定位是测试 ID 缺失还是应用崩溃Android 无法访问更新服务器确认adb reverse tcp:EXPO_PUBLIC_UPDATES_SERVER_PORT已执行执行器beforeAll会自动做updates.url中的主机在 Android 模拟器上应使用10.0.2.2指向宿主机iOS 与 Android 同时运行README 明确要求运行时只保留目标平台的一个模拟器执行器通过maestro -p指定平台但多设备仍可能干扰执行务必按文档要求只开一个测试更新服务器端口被占用执行器会先探测端口若已被占用则复用现有进程不会重复拉起若需调试可先手动执行./maestro/updates-server/start.ts测试结束后执行器会将其一并关闭测试工程使用旧代码脚手架通过npm packlink:引用仓库内最新源码若改动 expo-updates 源码后需重新运行create-eas-project.ts非 CI 下会自动删除旧工程重建再重新构建原生应用。七、总结expo-updates 的 E2E 测试体系由三块协同组成以 create-eas-project.ts 为代表的 TypeScript 脚手架负责生成使用仓库最新源码的测试工程、注入原生测试模块、配置代码签名与 app.json、以 server.ts 为核心的本地 mock 更新服务器精确控制每种 manifest 应答场景以及以 updates-e2e-enabled.yml 为入口的 Maestro 场景套件。配合 Updates API 手动测试项目开发者既能一键回归更新下载、校验、回滚等关键链路也能在真实 EAS 环境下手工验证 API 行为。按照本文的环境变量、命令顺序与排障要点操作即可在当前仓库中完整复现这套测试流程并在此基础上扩展自己的更新测试场景。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表