Detox 端到端测试接入 CI 的完整指南:Release 构建、模拟器清理与主流 CI 平台配置
【免费下载链接】DetoxGray box end-to-end testing and automation framework for mobile apps项目地址: https://gitcode.com/gh_mirrors/de/Detox
本文基于仓库中 20.x 版本官方文档 Preparing for CI 编写。该文档在仓库内被明确标注为outdated(过时)——附录中 Travis 与 Xcode 8.3 等示例偏旧,但"在 CI 上测试 Release 构建 + 测试结束后关闭模拟器"的核心方法论至今仍然适用。本文将原文档全部实操内容继承下来,并结合当前仓库的 CLI 源码、示例配置与 CI 脚本进行纵深解读,帮助你以当前仓库的实际情况为准落地一套可靠的 CI 流水线。
当移动 App 的端到端测试套件在本地稳定运行后,下一步就是把它接入 CI(持续集成)服务器:每次git push自动执行一遍回归测试,一旦新改动破坏了既有功能,CI 能第一时间发出警报。本文围绕 Detox 官方 CI 准备指南,讲解 CI 场景下与本地运行截然不同的两个关键点(Release 构建与模拟器清理),逐平台给出 Travis CI、Bitrise、GitLab CI 的可直接落地的配置模板,并结合仓库源码说明--cleanup、--headless等 CI 常用 CLI 参数的底层行为,以及 Android 模拟器环境在无界面 CI 机器上的搭建要点。
为什么 CI 上跑 Detox 与本地不同:两个关键差异
原文档开门见山地指出:Running Detox on CI is not that different from running it locally(在 CI 上运行 Detox 与本地运行并没有本质区别),但存在两个主要差异,这也是整套 CI 配置的核心出发点:
- 测试 Release 构建而不是 Debug 构建——Release 构建更接近用户最终拿到的产物,能捕获仅在优化/混淆后才暴露的问题(例如 Android 上 ProGuard 混淆导致的反射失效),也更贴近真实性能表现;
- 告诉 Detox 在测试结束后关闭模拟器——CI 是批量、反复运行的场景,若每次运行都残留一个未关闭的模拟器/模拟器进程,会持续占用 CI 机器资源,甚至导致后续任务因设备被占用而失败。
围绕这两点,原文档给出了两个实施步骤,并附上了三大 CI 平台的完整配置示例(见文末附录)。以下逐步展开。
Step 1:为 App 准备 Release 配置
要在 CI 上测试 Release 构建,首先需要在 Detox 配置中为 App 准备一个release 形态的 app 配置。如果还没有完成基础的项目接入,请先阅读前一篇教程 Project Setup(初始化、App/设备配置、构建 App 的全流程)。
Detox 采用静态配置文件描述apps、devices与configurations三组字典,配置名(如ios.sim.release)就是运行测试时的入口(详细结构见 配置总览)。仓库自带的端到端测试工程 detox/test/e2e/detox.config.js 就是一份同时覆盖 debug 与 release 的完整示例,其中 iOS 的 release 配置形如:
'ios.release': { type: 'ios.app', name: 'example', binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/example.app', build: 'set -o pipefail && export CODE_SIGNING_REQUIRED=NO && export RCT_NO_LAUNCH_PACKAGER=true && xcodebuild -workspace ios/example.xcworkspace -scheme example-ci -configuration Release -sdk iphonesimulator -derivedDataPath ios/build -quiet', arch: 'arm64', },几个值得注意的细节:
binaryPath指向Release 产物(Release-iphonesimulator/example.app),与 debug 的Debug-iphonesimulator产物路径区分开;build命令中export CODE_SIGNING_REQUIRED=NO用于模拟器场景跳过签名,export RCT_NO_LAUNCH_PACKAGER=true则避免在 Release 测试时拉起 React Native 打包器(Metro)——这正是"测试 Release 构建"的核心语义之一:不依赖开发时的打包服务;detox build本身没有任何额外逻辑,只是把配置里写好的build命令原样执行(参见 Project Setup 中的说明),因此 CI 上的构建产物与你本地detox build --configuration ios.sim.release完全一致。
Android 侧同理,仓库示例中定义了android.release(见 detox/test/e2e/detox.config.js),并通过androidBaseAppConfig('release')生成对应的构建命令与 APK 路径,随后在configurations中组合出android.emu.release配置。
Android Release 构建还有一个必须处理的坑:ProGuard。由于 Detox 在 Android 上依赖 Java 反射 API 与 React Native 集成,必须让 Detox 的原生代码豁免于 ProGuard 混淆,否则 Release 模式跑测试时会崩溃或无限挂起。在android/app/build.gradle的 release 构建类型中追加(详见 Project Setup 的 ProGuard 说明):
release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro" signingConfig signingConfigs.release }仓库内对应的规则文件位于 detox/android/detox/proguard-rules-app.pro,完整的 ProGuard 配置说明可参考 ProGuard 配置指南。
Step 2:在 CI 脚本中加入 build 与 test 命令
假设你的 CI 通过某个 shell 脚本执行任务,那么在项目根目录下加入以下两条命令即可:
detox build --configuration ios.sim.release detox test --configuration ios.sim.release原文档特别用 tip 提示:Make sure to shut down the simulator when your tests are over(确保测试结束后关闭模拟器)。落实到命令层面,就是在detox test时追加--cleanup参数:
detox test --configuration ios.sim.release --cleanup--cleanup与--headless:CI 上最常用的两个参数
在 CLI 源码 detox/local-cli/testCommand/builder.js 中可以看到这两个参数的正式定义:
-u, --cleanup:Shutdown simulator when test is over, useful for CI scripts, to make sure detox exits cleanly with no residue(测试结束后关闭模拟器,便于 CI 脚本干净退出、不留残留进程);-H, --headless:Launch device in headless mode. Useful when running on CI(以无界面模式启动设备,适合 CI)。
从实现上看,detox test本质上是一个"参数转环境变量再拉起测试运行器"的转发器(详见 docs/cli/test.md)。在 detox/local-cli/testCommand/TestRunnerCommand.js 的_buildEnvOverride方法中,CLI 参数会被翻译为环境变量传递给测试子进程:
DETOX_CLEANUP: cliConfig.cleanup, // TestRunnerCommand.js#L133 DETOX_HEADLESS: cliConfig.headless, // TestRunnerCommand.js#L143也就是说,detox test -u最终等价于在DETOX_CLEANUP=1的环境下运行测试运行器(如 Jest),设备生命周期管理逻辑会在测试结束后据此关闭模拟器/模拟器进程;-H同理,将DETOX_HEADLESS=1传给设备启动逻辑,在无显示器的 CI 机器上以-no-window方式启动 Android 模拟器。
除了这两个参数,docs/cli/test.md 还列出了一些 CI 场景常用的detox test选项,同样可在 builder.js 中找到定义:
| 参数 | 说明(CI 场景下的典型用途) |
|---|---|
-c, --configuration | 选择配置,例如ios.sim.release;若不传且配置只有一项,Detox 会自动使用它 |
-u, --cleanup | 测试结束后关闭模拟器,保证 CI 干净退出 |
-H, --headless | 无界面启动设备,适合无 GUI 的 CI 机器 |
-R, --retries <N> | 对单个失败的测试文件重新拉起测试运行器,直到通过或达到 N 次上限,可显著降低 CI 上的偶发失败 |
-l, --loglevel | 日志级别(fatal/error/warn/info/verbose/debug/trace),CI 排障时常用-l verbose |
--record-logs,--take-screenshots,--record-videos | 将日志、截图、录屏写入 artifacts 目录,便于 CI 失败时取证 |
-r, --reuse | 复用已安装的 App(不删除重装),加快重复运行速度 |
--gpu <mode> | Android 专属:以指定 GPU 模式启动模拟器(如swiftshader_indirect,软件渲染,适合无 GPU 的 CI) |
--jest-report-specs | Jest 场景实时输出每个 spec 的日志 |
另外,detox test会将未知参数原样转发给底层测试运行器,因此你可以在同一行命令里混合使用 Detox 参数与 Jest 参数,例如detox test -c ios.debug --showConfig会被翻译为DETOX_CONFIGURATION=ios.debug jest --showConfig。当参数名与测试运行器冲突时,可用--分隔符显式传递给运行器(详见 docs/cli/test.md)。
Android 端 CI:模拟器环境准备与 headless 运行
原文档直言:Setting up a CI environment capable of running Android tests isn't as trivial(搭建能跑 Android 测试的 CI 环境并不轻松)。难点主要在于:
- KVM 虚拟化支持:Android 模拟器依赖硬件虚拟化(KVM)才能获得可接受的性能。因此 CI 提供方自带的共享 runner 通常无法直接运行模拟器,你需要自建带 KVM 支持的 runner(例如选择提供嵌套虚拟化的云主机);
- 无界面运行:CI 机器通常没有显示器,模拟器必须以 headless(
-no-window)模式启动; - 环境的确定性与稳定性:Google API 镜像自带的 Google Play 服务、gboard 键盘等组件会占用大量 CPU、诱发测试不稳定,因此官方强烈建议使用AOSP 镜像(Android Open Source Project)而非 Google APIs 镜像来跑自动化测试。
AOSP 模拟器的完整搭建流程(Java 环境、Android SDK、AOSP 镜像安装、AVD 创建、模拟器快速启动快照、Test Butler 集成等)记录在 Android 开发与测试环境搭建指南 中,其中包括适合无界面 CI 机器的命令行安装方式:
# 安装不带 Google APIs 的系统镜像(AOSP) $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "system-images;android-28;default;x86_64" $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses # 创建 AVD $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd -n Pixel_API_28_AOSP -d pixel \ --package "system-images;android-28;default;x86_64" # 无界面启动模拟器(适合 Linux 无显示器的 CI 机器) $ANDROID_HOME/emulator/emulator -verbose -no-window -no-audio -gpu swiftshader_indirect @Pixel_API_28_AOSP &在 Detox 侧,headless 既可以通过 CLI 参数-H指定,也可以直接在设备配置中声明。仓库示例 detox/test/e2e/detox.config.js 的做法是跟随 CI 环境变量动态切换:
'android.emulator': { type: 'android.emulator', headless: Boolean(process.env.CI), // 在 CI 环境自动切换为无界面模式 device: { avdName: 'Pixel_3a_API_36', }, // ... },同理,仓库还会在 CI 环境自动调整测试运行器行为,例如 detox/test/e2e/detox.config.js 中:
testRunner: { args: { $0: process.env.CI ? 'nyc jest' : 'jest', config: 'e2e/jest.config.js', forceExit: process.env.CI ? true : undefined, // CI 上强制退出,避免挂起 _: ['e2e/'], }, detached: !!process.env.CI, retries: process.env.CI ? 1 : undefined, // CI 上失败自动重试 1 次 // ... },这种"用process.env.CI做条件判断"的模式,是让同一份 Detox 配置同时服务本地与 CI 的推荐做法。
附录:三大 CI 平台完整配置示例
以下三套配置均直接继承自原文档,供你在对应平台上落地时参考。注意:其中 Travis 示例属于文档标注为 outdated 的部分(基于 Xcode 8.3 时代),请以你实际使用的 CI 平台与工具链版本为准。
Travis CI
Travis 示例是"模拟器 + applesimutils + Node"的最简组合:在install阶段安装 Detox 在 iOS 模拟器上工作所需的applesimutils工具与 Node 环境,在script阶段依次执行构建与测试(带--cleanup):
language: objective-c osx_image: xcode8.3 branches: only: - master env: global: - NODE_VERSION=stable install: - brew tap wix/brew - brew install applesimutils - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash - export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm install $NODE_VERSION - nvm use $NODE_VERSION - nvm alias default $NODE_VERSION - npm install react-native-cli --global - npm install detox-cli --global script: - detox build --configuration ios.sim.release - detox test --configuration ios.sim.release --cleanupBitrise
Bitrise 是自动化 React Native 应用时常用的 CI 服务。做法是新建一个名为tests的 workflow,拆分为两个子 workflow:_tests_setup(拉取代码、安装 npm 依赖)与_detox_tests(全局安装detox-cli与react-native-cli、安装applesimutils、构建 Release App、运行 E2E 测试):
--- format_version: 1.1.0 default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git trigger_map: - push_branch: "*" workflow: tests workflows: _tests_setup: steps: - activate-ssh-key: {} - git-clone: inputs: - clone_depth: '' title: Git Clone Repo - script: inputs: - content: |- #!/bin/bash npm cache verify npm install title: Install NPM Packages before_run: after_run: _detox_tests: before_run: [] after_run: [] steps: - npm: inputs: - command: install -g detox-cli title: Install Detox CLI - npm: inputs: - command: install -g react-native-cli title: Install React Native CLI - script: inputs: - content: |- #!/bin/bash brew tap wix/brew brew install applesimutils title: Install Detox Utils - script: inputs: - content: |- #!/bin/bash detox build --configuration ios.sim.release title: Detox - Build Release App - script: inputs: - content: |- #!/bin/bash detox test --configuration ios.sim.release --cleanup title: Detox - Run E2E Tests tests: before_run: - _tests_setup - _detox_tests after_run: []GitLab CI(Android Only)
GitLab CI 的官方共享 runner 通常缺少 KVM 支持而无法运行 Android 模拟器,因此需要自建带 KVM 的 runner(云厂商通常通过提供嵌套虚拟化能力的实例类型来支持)。下面这份 job 定义做了三件关键的事:
- 准备模拟器:通过
sdkmanager安装system-images;android-27;default;x86_64(注意是default即 AOSP 镜像,而非google_apis)与emulator,再用avdmanager创建名为Nexus6P的 AVD; - 修复系统配置:调大 inotify 文件监视上限(
fs.inotify.max_user_watches),避免文件监听溢出;初始化/root/.android目录; - 以 headless 模式运行:
npx detox test -c android.emu.release.ci --headless。
detox_e2e: stage: test image: reactnativecommunity/react-native-android variables: before_script: - npm install envinfo detox-cli --global && envinfo # Increase file watcher limit, see more here: https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers#the-technical-details - echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf && sysctl -p - mkdir -p /root/.android && touch /root/.android/repositories.cfg # The Dockerimage provides two paths for sdkmanager and avdmanager, which the defaults are from $ANDROID_HOME/cmdline-tools # That is not compatible with the one that Detox is using ($ANDROID_HOME/tools/bin) - echo yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --channel=0 --verbose "system-images;android-27;default;x86_64" "emulator" # Nexus 6P, API 27, XXXHDPI - echo no | $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager --verbose create avd --force --name "Nexus6P" --package "system-images;android-27;default;x86_64" --sdcard 200M --device 11 - adb start-server script: - npx detox build -c android.emu.release.ci - npx detox test -c android.emu.release.ci --headless注意其中使用了配置名android.emu.release.ci,这意味着你需要在.detoxrc.js中预先定义好这个配置(包括指向 AOSP 模拟器 AVD 的设备定义与 release 形态的 app 定义),Detox 才能据此完成构建与测试。
仓库自身的 CI 实践参考
当前仓库的 CI 脚本同样是这套方法论的落地样板,可以作为你设计自身 CI 流水线的参考:
- scripts/ci.ios.sh 展示了 iOS 侧在 CI 上的完整执行序列:先用
xcrun simctl list预热模拟器(解决重置构建环境后模拟器不可见的问题),再执行yarn build:ios与yarn e2e:ios; - detox/test/e2e/detox.config.js 集中体现了"本地/CI 双模式"的配置技巧:
headless: Boolean(process.env.CI)、retries: process.env.CI ? 1 : undefined、forceExit: process.env.CI ? true : undefined等; - 同时提供 scripts/ci.android.sh、scripts/ci.sh 等脚本,涵盖 iOS/Android 双端的 CI 执行与覆盖率收集。
小结
把 Detox 端到端测试接入 CI 的完整套路可以浓缩为四步:
- 准备 Release 配置——在
.detoxrc.js中为 iOS/Android 分别定义 release 形态的 app 配置(含正确的构建命令、产物路径,Android 还需处理 ProGuard 豁免); - 编写 CI 脚本——按
detox build --configuration <release配置>→detox test --configuration <release配置> --cleanup的顺序执行; - 处理设备生命周期——用
--cleanup保证测试结束即关闭模拟器、用--headless(或配置中的headless: Boolean(process.env.CI))适配无界面机器,用--retries降低偶发失败; - Android 单独准备环境——自建带 KVM 的 runner,安装 AOSP 镜像并创建 AVD,必要时用
--gpu swiftshader_indirect等参数适配无 GPU 环境。
更完整的 CLI 参数与配置说明,可继续阅读 detox test 命令文档、配置总览 与 Android 环境搭建指南。
【免费下载链接】DetoxGray box end-to-end testing and automation framework for mobile apps项目地址: https://gitcode.com/gh_mirrors/de/Detox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考