fastlane run_tests(scan)Action 详解:在模拟器上自动化运行 iOS/Mac 应用测试
本篇围绕 fastlane 仓库中的 run_tests action 文档展开,它介绍的就是 scan——一条命令在模拟器上运行 iOS 与 Mac 应用测试的完整方案。读完后你将掌握 fastlane scan 的用法、Scanfile 配置方式、全部关键参数(含 SCAN_* 环境变量)的含义与默认值,并理解其底层如何拼装 xcodebuild 命令、解析测试结果与生成报告。
为什么需要 run_tests / scan
文档给出的对比非常直观:不借助 scan 时,你需要手写一条完整的 xcodebuild 命令来跑测试:
xcodebuild \
-workspace MyApp.xcworkspace \
-scheme "MyApp" \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 6,OS=8.1' \
test
而它的原始输出是一大段 clang 编译参数与 DerivedData 路径,几乎无法阅读。通常还得再接一个格式化工具把输出转成 HTML:
set -o pipefail &&
xcodebuild \
-workspace MyApp.xcworkspace \
-scheme "MyApp" \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 6,OS=8.1' \
test \
| xcpretty \
-r "html" \
-o "tests.html"
有了 scan 之后,这一切被压缩成一行:
fastlane scan
在 fastlane 源码中,scan 与 run_tests 指向同一个 action:run_tests.rb 定义了 RunTestsAction,而 scan.rb 中 ScanAction < RunTestsAction 仅是一个别名。该 action 支持 iOS 与 Mac 平台(is_supported? 返回 [:ios, :mac]),归类于 :testing。
文档列出的核心特性,都能在源码中找到对应实现:
| 特性 | 源码依据 |
|---|---|
| 运行测试时有漂亮的内联构建输出 | 命令管道默认接 xcpretty/xcbeautify,见 test_command_generator.rb 的 pipe 方法 |
| 合理默认值:自动探测项目、scheme 等 | detect_values.rb 的 DetectValues.set_additional_default_values |
| 支持 HTML、JSON、JUnit 报告 | output_types 选项,默认 "html,junit",见 options.rb |
| 处理 Xcode 重复的模拟器 | detect_simulator 按机型 + 系统版本精确匹配模拟器 |
| 动态配置:参数 + 环境变量 | 每个选项均注册了 SCAN_* 环境变量,如 SCAN_SCHEME、SCAN_WORKSPACE |
| Slack 测试结果通知 | slack_poster.rb,由 runner.rb 的 handle_results 调用 |
| 常见测试错误诊断(如模拟器无响应) | error_handler.rb |
原始 xcodebuild 输出保存在 ~/Library/Logs/scan |
命令尾部 tee 到 xcodebuild_log_path |
基本用法
最简用法只需一条命令:
fastlane scan
如果需要更多控制,可以在命令行直接传参:
fastlane scan --workspace "Example.xcworkspace" --scheme "AppName" --device "iPhone 6" --clean
使用非默认 Xcode 安装时,可定义 DEVELOPER_DIR:
DEVELOPER_DIR="/Applications/Xcode6.2.app" scan
在 Fastfile 中对多台设备并行测试:
scan(
workspace: "Example.xcworkspace",
devices: ["iPhone 6s", "iPad Air"]
)
文档还提到两个实用入口:
fastlane action scan:列出全部可用参数;- 原始
xcodebuild输出可在~/Library/Logs/scan(对应buildlog_path选项,默认为~/Library/Logs/scan)中查看。
参数在源码中的定义
所有参数集中定义在 options.rb 的 Scan::Options.available_options 中,run_tests action 通过 CommanderGenerator 生成帮助文档(见 run_tests.rb 的 available_options)。按用途分组的核心参数如下(env_name 均可在 CI 中以环境变量形式覆盖):
项目与 scheme
| 参数 | 环境变量 | 说明 |
|---|---|---|
workspace |
SCAN_WORKSPACE |
工作区路径,verify_block 会校验目录存在性 |
project |
SCAN_PROJECT |
项目路径,必须为 .xcodeproj |
package_path |
SCAN_PACKAGE_PATH |
Swift Package 路径(与 project 互斥) |
scheme |
SCAN_SCHEME |
scheme 名,需标记为 Shared |
设备与模拟器管理
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
device / devices |
SCAN_DEVICE / SCAN_DEVICES |
— | 单台/多台设备,二者互斥 |
ensure_devices_found |
SCAN_ENSURE_DEVICES_FOUND |
false |
找不到指定设备时直接报错 |
force_quit_simulator |
SCAN_FORCE_QUIT_SIMULATOR |
false |
运行前 killall Simulator |
reset_simulator |
SCAN_RESET_SIMULATOR |
false |
运行前抹掉模拟器 |
disable_slide_to_type |
SCAN_DISABLE_SLIDE_TO_TYPE |
true |
关闭“滑动解锁”输入提示 |
prelaunch_simulator |
SCAN_PRELAUNCH_SIMULATOR |
— | 提前启动第一个模拟器 |
reinstall_app |
SCAN_REINSTALL_APP |
false |
运行前卸载被测应用(需配 app_identifier) |
测试选择与测试计划
| 参数 | 环境变量 | 说明 |
|---|---|---|
only_testing |
SCAN_ONLY_TESTING |
仅运行指定测试,格式 TestTarget[/TestSuite[/TestCase]] |
skip_testing |
SCAN_SKIP_TESTING |
跳过指定测试 |
testplan |
SCAN_TESTPLAN |
使用 scheme 关联的 test plan |
only_test_configurations / skip_test_configurations |
SCAN_ONLY_TEST_CONFIGURATIONS 等 |
test plan 配置的包含/排除 |
xctestrun |
SCAN_XCTESTRUN |
用现成的 .xctestrun 文件运行 |
number_of_retries |
SCAN_NUMBER_OF_RETRIES |
失败重试次数,默认 0;Xcode 13+ 会转换为 -retry-tests-on-failure -test-iterations N |
输出与报告
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
output_directory |
SCAN_OUTPUT_DIRECTORY |
fastlane/test_output |
报告输出目录 |
output_types |
SCAN_OUTPUT_TYPES |
html,junit |
报告类型,如 html,junit,json-compilation-database |
output_files |
SCAN_OUTPUT_FILES |
— | 与 output_types 顺序对应的自定义文件名 |
output_style |
SCAN_OUTPUT_STYLE |
— | standard/basic/rspec/raw,raw 表示不接 formatter |
xcodebuild_formatter |
SCAN_XCODEBUILD_FORMATTER |
检测到 xcbeautify 则用它,否则 xcpretty |
输出格式化工具 |
derived_data_path |
SCAN_DERIVED_DATA_PATH |
— | 构建产物目录,不设置时自动探测 |
result_bundle / result_bundle_path |
SCAN_RESULT_BUNDLE* |
false |
生成 .xcresult 结果包 |
should_zip_build_products |
SCAN_SHOULD_ZIP_BUILD_PRODUCTS |
false |
压缩 DerivedData 的 Build/Products |
output_xctestrun |
SCAN_OUTPUT_XCTESTRUN |
false |
拷贝生成的 settings.xctestrun 到输出目录 |
suppress_xcode_output |
SCAN_SUPPRESS_XCODE_OUTPUT |
— | 不在 stdout 打印 xcodebuild 输出(日志仍落盘) |
include_simulator_logs |
SCAN_INCLUDE_SIMULATOR_LOGS |
false |
收集模拟器系统日志到输出目录 |
构建相关
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
clean |
SCAN_CLEAN |
false |
构建前 clean |
code_coverage |
SCAN_CODE_COVERAGE |
— | 开启代码覆盖率 |
address_sanitizer / thread_sanitizer |
— | — | ASan/TSan,二者互斥 |
build_for_testing / test_without_building |
SCAN_BUILD_FOR_TESTING / SCAN_TEST_WITHOUT_BUILDING |
— | 只构建不测试 / 跳过构建直接测试(需 derived_data_path),二者互斥 |
configuration |
SCAN_CONFIGURATION |
动态探测 | 构建配置 |
xcargs |
SCAN_XCARGS |
— | 透传额外 xcodebuild 参数 |
xcconfig |
SCAN_XCCONFIG |
— | 附加 .xcconfig 文件 |
xcodebuild_command |
GYM_XCODE_BUILD_COMMAND |
env NSUnbufferedIO=YES xcodebuild |
可整体替换构建命令 |
Slack 通知:slack_url(SLACK_URL,敏感值)、slack_channel、slack_message、slack_only_on_failure、skip_slack 等,用于测试结果推送到 Slack。
底层调用链与命令生成
run_tests 的入口在 run_tests.rb 的 RunTestsAction.run:它 require 'scan' 后创建 Scan::Manager 并执行 work(values)。
- manager.rb 的
Manager#work先设置Scan.config(触发DetectValues自动补全缺失值),打印“Summary for scan”参数表,然后委托Runner#run。 - runner.rb 的
test_app依次处理模拟器准备(force quit、reset、disable slide-to-type)、prelaunch_simulators、reinstall_app,然后进入execute:由TestCommandGenerator#generate生成最终命令,经FastlaneCore::CommandExecutor.execute执行;失败时若有重试配额,retryable_tests会从错误输出中解析出具体失败的测试用例,设置only_testing后重跑,只重试失败项。
生成的命令结构由 test_command_generator.rb 拼装,顺序为 prefix + xcodebuild_command + options + actions + pipe:
prefix:set -o pipefail &&,若配置了package_path还会cd到该目录;options:-workspace/-scheme、-sdk、destination(由DetectValues生成)、-derivedDataPath、-resultBundlePath、并行测试参数(-parallel-testing-enabled等)、sanitizer 开关、-only-testing:/-skip-testing:等;actions:clean(可选)、build-for-testing、test-without-building或默认build+test;pipe:| tee '<日志路径>'落盘原始输出,再按xcodebuild_formatter接xcpretty或xcbeautify;output_style: 'raw'时跳过 formatter。
默认值的“聪明”之处集中在 detect_values.rb:自动加载 Scanfile、探测工程并 select_scheme;未指定设备时按平台兜底(iOS 默认选 iPhone 5s、tvOS 选 Apple TV 1080p 的最高兼容模拟器),并根据 Deployment Target 过滤模拟器版本;destination 最终统一为 platform=<OS> Simulator,id=<UDID> 形式(run_rosetta_simulator: true 时追加 arch=x86_64);derived_data_path 未设置时会从 BUILT_PRODUCTS_DIR 反推 DerivedData 根目录。
Scanfile:保存常用配置
文档建议将默认参数存入 Scanfile,避免每次手动传参。运行 fastlane scan init 即可创建,例如:
scheme("Example")
devices(["iPhone 6s", "iPad Air"])
clean(true)
output_types("html")
从源码看,Scanfile 的加载有两处兜底:module.rb 中 scanfile_name 固定为 "Scanfile",DetectValues.set_additional_default_values 会先在当前目录加载一次,探测到工程路径后若发现工程目录里还有另一份 Scanfile 会再次加载,即工程目录下的 Scanfile 优先于当前目录。
测试结果、返回值与 lane_context
测试结束后,Runner#trainer_test_results(runner.rb)会定位本次运行新增的 .xcresult(通过运行前后 DerivedData 内 Logs/Test/*.xcresult 的差集),交给 trainer 的 Trainer::TestParser.auto_convert 解析,并统计:
number_of_tests、number_of_failures、number_of_retries、number_of_skipped,以及排除重试后的对应计数。
控制台会打印 “Test Results” 表格;存在失败时,fail_build: true(默认)会 UI.test_failure! 中止 lane,fail_build: false 则仅报错不中断——适合搭配 trainer 使用。此外 open_report 会在非 CI 环境自动打开 HTML 报告,SlackPoster 推送结果。
run_tests 作为 lane 中的一步,返回值与共享值(见 run_tests.rb 的 return_value 与 output):
- 返回值:包含
:number_of_tests、:number_of_failures、:number_of_retries、:number_of_tests_excluding_retries、:number_of_failures_excluding_retries的 Hash; lane_context[SharedValues::SCAN_GENERATED_XCRESULT_PATH]:生成的.xcresult路径;lane_context[SharedValues::SCAN_GENERATED_PLIST_FILE(S)]:新增的TestSummaries.plist;lane_context[SharedValues::SCAN_DERIVED_DATA_PATH]与lane_context[SharedValues::SCAN_ZIP_BUILD_PRODUCTS_PATH]:DerivedData 路径与压缩后的构建产物路径。
值得注意的实现细节:RunTestsAction.run 会专门捕获 FastlaneBuildFailure 并重新抛出,确保即使 fail_build: false,编译/构建错误也不会被静默吞掉,fail_build 只抑制测试失败。
在 fastlane 工作流中自动化
scan 与 fastlane 原生集成,文档给出的 lane 示例:
lane :test do
scan(scheme: "Example")
end
run_tests 的 example_code(run_tests.rb)还展示了几个典型组合,可直接复制使用:
# 别名等价写法
scan # alias for "run_tests"
run_tests(
workspace: "App.xcworkspace",
scheme: "MyTests",
clean: false
)
# 仅构建测试包(不运行)
run_tests(
derived_data_path: "my_folder",
build_for_testing: true
)
# 复用已有构建产物,跳过构建
run_tests(
derived_data_path: "my_folder",
test_without_building: true
)
# 直接使用现成的 xctestrun 包
run_tests(
xctestrun: "/path/to/mytests.xctestrun"
)
在 CI 环境中,由于 Helper.ci? 为真,HTML 报告不会自动打开,建议显式指定 output_directory 与 output_types,并将原始日志(~/Library/Logs/scan)作为构建产物归档,便于排查偶发测试失败。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

