首页
/ fastlane run_tests(scan)Action 详解:在模拟器上自动化运行 iOS/Mac 应用测试

fastlane run_tests(scan)Action 详解:在模拟器上自动化运行 iOS/Mac 应用测试

2026-09-05 15:08:37作者:翟萌耘Ralph

本篇围绕 fastlane 仓库中的 run_tests action 文档展开,它介绍的就是 scan——一条命令在模拟器上运行 iOS 与 Mac 应用测试的完整方案。读完后你将掌握 fastlane scan 的用法、Scanfile 配置方式、全部关键参数(含 SCAN_* 环境变量)的含义与默认值,并理解其底层如何拼装 xcodebuild 命令、解析测试结果与生成报告。

scan 在模拟器上运行测试的控制台输出

为什么需要 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 源码中,scanrun_tests 指向同一个 action:run_tests.rb 定义了 RunTestsAction,而 scan.rbScanAction < RunTestsAction 仅是一个别名。该 action 支持 iOS 与 Mac 平台(is_supported? 返回 [:ios, :mac]),归类于 :testing

文档列出的核心特性,都能在源码中找到对应实现:

特性 源码依据
运行测试时有漂亮的内联构建输出 命令管道默认接 xcpretty/xcbeautify,见 test_command_generator.rbpipe 方法
合理默认值:自动探测项目、scheme 等 detect_values.rbDetectValues.set_additional_default_values
支持 HTML、JSON、JUnit 报告 output_types 选项,默认 "html,junit",见 options.rb
处理 Xcode 重复的模拟器 detect_simulator 按机型 + 系统版本精确匹配模拟器
动态配置:参数 + 环境变量 每个选项均注册了 SCAN_* 环境变量,如 SCAN_SCHEMESCAN_WORKSPACE
Slack 测试结果通知 slack_poster.rb,由 runner.rbhandle_results 调用
常见测试错误诊断(如模拟器无响应) error_handler.rb
原始 xcodebuild 输出保存在 ~/Library/Logs/scan 命令尾部 teexcodebuild_log_path

scan 的 Slack 测试结果通知

基本用法

最简用法只需一条命令:

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.rbScan::Options.available_options 中,run_tests action 通过 CommanderGenerator 生成帮助文档(见 run_tests.rbavailable_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/rawraw 表示不接 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_urlSLACK_URL,敏感值)、slack_channelslack_messageslack_only_on_failureskip_slack 等,用于测试结果推送到 Slack。

底层调用链与命令生成

run_tests 的入口在 run_tests.rbRunTestsAction.run:它 require 'scan' 后创建 Scan::Manager 并执行 work(values)

  • manager.rbManager#work 先设置 Scan.config(触发 DetectValues 自动补全缺失值),打印“Summary for scan”参数表,然后委托 Runner#run
  • runner.rbtest_app 依次处理模拟器准备(force quit、reset、disable slide-to-type)、prelaunch_simulatorsreinstall_app,然后进入 execute:由 TestCommandGenerator#generate 生成最终命令,经 FastlaneCore::CommandExecutor.execute 执行;失败时若有重试配额,retryable_tests 会从错误输出中解析出具体失败的测试用例,设置 only_testing 后重跑,只重试失败项。

生成的命令结构由 test_command_generator.rb 拼装,顺序为 prefix + xcodebuild_command + options + actions + pipe

  • prefixset -o pipefail &&,若配置了 package_path 还会 cd 到该目录;
  • options-workspace/-scheme-sdkdestination(由 DetectValues 生成)、-derivedDataPath-resultBundlePath、并行测试参数(-parallel-testing-enabled 等)、sanitizer 开关、-only-testing:/-skip-testing: 等;
  • actionsclean(可选)、build-for-testingtest-without-building 或默认 build + test
  • pipe| tee '<日志路径>' 落盘原始输出,再按 xcodebuild_formatterxcprettyxcbeautifyoutput_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.rbscanfile_name 固定为 "Scanfile"DetectValues.set_additional_default_values 会先在当前目录加载一次,探测到工程路径后若发现工程目录里还有另一份 Scanfile 会再次加载,即工程目录下的 Scanfile 优先于当前目录

测试结果、返回值与 lane_context

测试结束后,Runner#trainer_test_resultsrunner.rb)会定位本次运行新增的 .xcresult(通过运行前后 DerivedData 内 Logs/Test/*.xcresult 的差集),交给 trainer 的 Trainer::TestParser.auto_convert 解析,并统计:

  • number_of_testsnumber_of_failuresnumber_of_retriesnumber_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.rbreturn_valueoutput):

  • 返回值:包含 :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_testsexample_coderun_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_directoryoutput_types,并将原始日志(~/Library/Logs/scan)作为构建产物归档,便于排查偶发测试失败。

登录后查看全文
热门项目推荐
相关项目推荐