☰
Instructions 项目 fastlane 自动化实战指南:构建、单元测试、UI 测试与快照测试的完整流水线解析
2026/10/15 20:29:32 网站建设 项目流程
  • 移动开发
  • UI组件

【免费下载链接】Instructions

Create walkthroughs and guided tours (coach marks) in a simple way, with Swift.

项目地址:https://gitcode.com/gh_mirrors/in/Instructions
点击查看免费下载

本文以 fastlane/README.md 与 fastlane/Fastfile 为主体,系统讲解 iOS 开源库 Instructions(Swift 编写的 coach marks / 引导页框架)如何使用 fastlane 实现「构建框架与示例工程、运行单元与 UI 测试、生成并校验快照」的自动化流水线。读完本文,你将掌握每个 lane 的调用方式、参数含义与底层实现,能够基于这份配置搭建属于自己的 iOS 持续集成流程,并理解快照测试从「录制参考图」到「回归校验」的完整工作机制。

一、fastlane 在 Instructions 项目中的角色

Instructions 是一个同时以 SwiftPM(见 Package.swift)、CocoaPods(见 Instructions.podspec)等形式分发的 iOS 框架仓库,其工程以 workspace 形式组织(见 Instructions.xcworkspace/contents.xcworkspacedata),包含四个可构建的 target scheme:

Scheme用途
Instructions框架本体
Instructions App Extensions面向 App Extensions 的框架变体
Instructions Example演示示例 App(含单元测试、UI 测试与快照测试)
Instructions App Extensions Example演示键盘扩展的示例工程

fastlane 的作用就是把这四类 scheme 的「构建」与「测试」全部收敛为一条条可复用的 lane,让本地开发与 CI 都能用同一套命令完成校验。fastlane/README.md 是 fastlane 在每次运行后自动重新生成的文档,它忠实列出了当前 Fastfile 中暴露的所有 lane 与对应命令——它是 Fastfile 的「自动索引」,本文将以这份清单为骨架,结合 fastlane/Fastfile 的源码逐层拆解。

二、环境准备与依赖安装

在运行任何 lane 之前,需要先完成两件事:

  1. 安装最新版本的 Xcode Command Line Tools:
xcode-select --install
  1. 通过 Bundler 安装 fastlane 及配套依赖。仓库的 Gemfile 声明了运行全部流水线所需的两个 gem:
# frozen_string_literal: true source "https://rubygems.org" gem "fastlane" gem 'slather'
  • fastlane:负责全部构建、测试 lane;
  • slather:用于生成代码覆盖率报告(CI 中通过slather coverage -x --build-directory Build/生成 cobertura 格式覆盖率并上报)。

Gemfile.lock 锁定了当时依赖树(fastlane 2.216.0、slather 2.7.5,支持 arm64-darwin 与 x86_64-linux 平台)。安装命令:

bundle install

之后所有 lane 统一通过 Bundler 前缀调用,保证 CI 与本地使用完全一致的 fastlane 版本:

bundle exec fastlane ios <lane_name>

说明:仓库的 fastlane/Appfile 目前仅保留了注释模板(app_identifier与apple_id均未启用)。原因在于本仓库的 lane 只执行构建与测试,不涉及上传 App Store / TestFlight 等需要 Apple 账号鉴权的操作,因此不需要在 Appfile 中配置 bundle identifier 与 Apple ID。若你要在自己的项目中复用这套 lane,再按需取消注释即可。

三、lane 全景一览

fastlane/README.md 列出的全部 lane 及其命令、用途如下:

Lane 名称命令用途
ios build_all[bundle exec] fastlane ios build_all构建全部 target
ios build_frameworks[bundle exec] fastlane ios build_frameworks构建框架(Instructions 及其 App Extensions 变体)
ios build_examples[bundle exec] fastlane ios build_examples构建示例工程
ios build[bundle exec] fastlane ios build构建指定的 scheme
ios generate_snapshots[bundle exec] fastlane ios generate_snapshots为所有模拟器生成新的快照参考图
ios test_all[bundle exec] fastlane ios test_all运行完整测试套件
ios test_snapshots[bundle exec] fastlane ios test_snapshots运行快照回归测试
ios test_unit_and_ui[bundle exec] fastlane ios test_unit_and_ui运行单元测试与 UI 测试

其中build_all与test_all是复合 lane,分别聚合了「两个构建子流程」与「两个测试子流程」,构成了仓库的核心 CI 路径。

四、Fastfile 全局配置解读

进入各 lane 之前,先看 fastlane/Fastfile 中定义的全局常量,它们贯穿所有 lane:

default_platform(:ios) platform :ios do DEVICES = [ "iPad (10th generation)", "iPhone 15 Pro", "iPhone SE (3rd generation)" ] WORKSPACE = "Instructions.xcworkspace" INSTRUCTIONS_SCHEME = "Instructions" APP_EXTENSION_INSTRUCTIONS_SCHEME = "Instructions App Extensions" EXAMPLE_SCHEME = "Instructions Example" APP_EXTENSION_EXAMPLE_SCHEME = "Instructions App Extensions Example" DERIVED_DATA_PATH = "Build/"
  • DEVICES:测试覆盖的三款模拟器设备——iPad(第 10 代)、iPhone 15 Pro、iPhone SE(第 3 代),同时覆盖了 iPad 与不同屏幕形态的 iPhone,确保快照测试在横竖屏、异形屏(刘海)等场景下都有参考;
  • WORKSPACE:统一使用Instructions.xcworkspace而非单一 xcodeproj,以便同时编译框架与两个示例工程;
  • 四个 scheme 常量:区分「框架本体 / 框架扩展变体 / 示例 App / 扩展示例 App」四类 target;
  • DERIVED_DATA_PATH:把 DerivedData 固定到仓库根目录的Build/,这一约定同时被 CI 中的 slather 覆盖率收集步骤复用(--build-directory Build/)。

五、构建 lane:从复合到单点

5.1 build_all:一键构建全部 target

desc "Build all targets" lane :build_all do build_frameworks build_examples end

build_all依次调用build_frameworks与build_examples,是「全量构建」的入口,等价于把框架与示例工程全部编译一遍,用于验证整个仓库可构建。

5.2 build_frameworks 与 build_examples

desc "Build frameworks" lane :build_frameworks do build(scheme: INSTRUCTIONS_SCHEME) build(scheme: APP_EXTENSION_INSTRUCTIONS_SCHEME) end desc "Build examples" lane :build_examples do build(scheme: EXAMPLE_SCHEME) build(scheme: APP_EXTENSION_EXAMPLE_SCHEME) end

两个 lane 都只是以不同的 scheme 参数调用底层的buildlane:

  • build_frameworks验证框架本体(Instructions)与 App Extensions 变体(Instructions App Extensions)能否编译通过;
  • build_examples验证两个示例工程(Instructions Example、Instructions App Extensions Example)能否编译通过。

5.3 build:单一 scheme 的构建实现

desc "Build the given scheme" lane :build do |values| build_app( workspace: WORKSPACE, scheme: values[:scheme], clean: true, skip_archive: true, skip_codesigning: true ) end

build接收values[:scheme]作为参数,通过 fastlane 内建的build_appaction 完成编译。三个关键参数的含义:

  • clean: true:每次构建都执行 clean,确保产物不受增量编译缓存污染;
  • skip_archive: true:仅编译、不归档,因为这里只需要验证「能构建」,不需要产出可分发的归档包;
  • skip_codesigning: true:跳过代码签名。库的构建验证不涉及真机安装或发布,跳过签名即可在无开发者证书的 CI 环境直接运行。

六、测试 lane:三层测试体系

6.1 test_all:完整测试套件入口

desc "Run full test suite" lane :test_all do test_snapshots() test_unit_and_ui() end

test_all串联两条测试流水线:先跑快照测试,再跑单元与 UI 测试。这也是 .github/workflows/continuous-integration.yml 中 CI 主流程调用的 lane(bundle exec fastlane test_all)。

6.2 test_unit_and_ui:单元测试 + UI 测试

desc "Run Unit & UI tests" lane :test_unit_and_ui do run_tests( workspace: WORKSPACE, scheme: EXAMPLE_SCHEME, testplan: "Unit+UI", devices: DEVICES[0], prelaunch_simulator: true, derived_data_path: DERIVED_DATA_PATH, ) end

该 lane 使用run_tests在Instructions Examplescheme 下执行名为Unit+UI的测试计划(testplan)。Examples/Unit+UI.xctestplan 定义了这个计划的测试目标:

  • InstructionsExampleUITests(UI 测试,parallelizable)
  • InstructionsUnitTests(单元测试,parallelizable)

两者均标记为parallelizable,可在模拟器上并行执行以缩短总耗时。该 lane 只在DEVICES[0](iPad 第 10 代)上运行;prelaunch_simulator: true用于在测试开始前预热模拟器,减少首轮启动带来的超时抖动。

6.3 test_snapshots:快照回归测试

desc "Run snapshot tests" lane :test_snapshots do run_tests( workspace: WORKSPACE, scheme: EXAMPLE_SCHEME, testplan: "Snapshot", only_test_configurations: "Test Snapshots", devices: DEVICES, prelaunch_simulator: true, derived_data_path: DERIVED_DATA_PATH, ) end

快照测试在全部三台模拟器(DEVICES)上运行,使用Snapshot测试计划中的Test Snapshots配置。Examples/Snapshot.xctestplan 中该配置的关键是两条环境变量(同时出现在defaultOptions中,作用于整个计划):

  • FB_REFERENCE_IMAGE_DIR→$(SRCROOT)/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages:参考图目录,即$(SRCROOT)/Example/Snapshot Tests/Supporting Files/Snapshots/ReferenceImages_64(仓库中以_64后缀落盘);
  • IMAGE_DIFF_DIR→$(SRCROOT)/Example/Snapshot Tests/Supporting Files/Snapshots/FailureDiffs:测试失败时的差异图目录。

该计划的测试目标是InstructionsSnapshotTests(同样 parallelizable),入口文件为 Examples/Example/Snapshot Tests/DefaultExampleSnapshotTests.swift。它的setUp中有两行与 fastlane 流水线直接呼应的逻辑:

fileNameOptions = [.screenScale] recordMode = ProcessInfo.processInfo.environment["RECORD_MODE"] != nil

也就是说:测试进程是否处于「录制模式」,完全由环境变量RECORD_MODE是否存在决定——这正是下面generate_snapshotslane 切换测试行为的开关。

6.4 generate_snapshots:生成/更新参考图

desc "Generate new snapshots for all simulators" lane :generate_snapshots do run_tests( workspace: WORKSPACE, scheme: EXAMPLE_SCHEME, testplan: "Snapshot", only_test_configurations: "Generate Snapshots", devices: DEVICES ) end

与test_snapshots的差异只有一处:only_test_configurations从Test Snapshots变为Generate Snapshots。后者的配置(见 Examples/Snapshot.xctestplan)额外注入了:

"environmentVariableEntries" : [ { "key" : "RECORD_MODE", "value" : "true" }, ... ]

因此当快照测试用例检测到RECORD_MODE=true时,DefaultExampleSnapshotTests.swift 会把recordMode置为真,测试不再比对参考图,而是把当前画面写盘作为新的参考图。这就是 Instructions 快照工作流的核心闭环:

改动 UI / 布局或新增测试用例 │ ▼ fastlane ios generate_snapshots (RECORD_MODE=true,录制新参考图) │ ▼ 提交新生成的 ReferenceImages │ ▼ fastlane ios test_snapshots (无 RECORD_MODE,回归比对)

快照用例本身覆盖了两种演示维度(见 DefaultExampleSnapshotTests.swift):

  • 三种展示上下文:independentWindow(独立窗口)、sameWindow(同窗口)、controller(控制器内);
  • 两种行为模式:testFlowIn*(教练标记依次推进的完整流程)与testRotationIn*(流程中途旋转屏幕)。

每个用例对同一场景连续截取 5 张快照(for _ in 0..<5),比对时使用perPixelTolerance: 0.1与overallTolerance: 0.002两档容差(见 DefaultExampleSnapshotTests.swift)。此外,为保证快照稳定,测试会通过--SnapshotTests启动参数隐藏状态栏(见 Examples/Example/Snapshot Tests/Extensions/XCUIApplication+Launch.swift),并借助 DeviceKit 扩展按设备型号精确裁剪状态栏 / Home Indicator 区域(见 Examples/Example/Snapshot Tests/Extensions/UIImage+Extensions.swift),确保参考图不受系统 UI 干扰、可跨设备复现。

七、与 CI 的集成:一条命令跑完整套件

仓库的 .github/workflows/continuous-integration.yml 展示了这条流水线在实际 CI 中的用法。其testjob 在macos-13(Xcode 14.3.1)上执行:

bundle install bundle exec fastlane test_all

即一次调用聚合了test_snapshots+test_unit_and_ui。CI 中还有两个与 fastlane 配置强相关的配套环节:

  1. 失败差异图上传:若test_all失败,把Examples/Example/Snapshot Tests/Supporting Files/Snapshots/FailureDiffs/作为 artifact 上传,方便人工定位快照偏差;
  2. 覆盖率收集:通过slather coverage -x --build-directory Build/生成 cobertura 格式覆盖率,再由 CodeClimate 上报。其配置见 .slather.yml——source_directory: Sources、output_directory: Tests,并忽略Examples/*(示例代码不计入框架覆盖率)。

此外 CI 还通过独立的 InstructionsPackageTest 仓库分别验证 SwiftPM、CocoaPods、Carthage 三种安装方式下的导入与构建,与本仓库 Fastfile 的构建 lane 形成互补:Fastfile 管「仓库内部全量构建 + 测试」,CI 额外管「第三方集成方式可用性」。

八、常见问题与实操建议

  1. 为什么所有命令都要加bundle exec?fastlane/README.md 中每个 lane 的调用方式都写作[bundle exec] fastlane ...,方括号表示可选。推荐始终使用bundle exec,以锁定 Gemfile.lock 中记录的 fastlane 2.216.0 版本,避免本机全局 fastlane 版本差异导致行为不一致。

  2. 改完 UI 后快照测试红了怎么办?这是快照测试的正常预期:UI 变化导致画面与参考图不一致。正确的处理顺序是:先人工核对FailureDiffs目录下的差异图,确认改动符合预期,再运行bundle exec fastlane ios generate_snapshots重新录制参考图,最后提交新参考图并跑一遍ios test_snapshots验证。

  3. 只想快速验证一个 scheme 能否编译?运行bundle exec fastlane ios build scheme:YourScheme。注意build是唯一接收参数的 lane,其余 lane 均已固化 scheme。

  4. 新增测试用例后如何纳入流水线?单元测试加入InstructionsUnitTests、UI 测试加入InstructionsExampleUITests即可被 Examples/Unit+UI.xctestplan 自动拾取;新增快照用例则需在InstructionsSnapshotTests中编写并先跑generate_snapshots生成参考图。

九、总结

本文以 fastlane 自动生成的 lane 清单为索引,逐条对照 fastlane/Fastfile 的实现,完整还原了 Instructions 仓库的自动化流水线:四条构建 lane(build_all/build_frameworks/build_examples/build)负责四个 scheme 的编译验证;四条测试 lane(test_all/test_snapshots/test_unit_and_ui/generate_snapshots)分别承载单元测试、UI 测试与快照测试,其中RECORD_MODE环境变量配合 Examples/Snapshot.xctestplan 的两种配置,实现了「录制参考图 ↔ 回归比对」的可逆闭环。这套配置同时服务于本地开发与 GitHub Actions 持续集成,可以作为任何 iOS 库项目搭建「构建 + 测试 + 快照 + 覆盖率」一体化流水线的直接范本。

  • 移动开发
  • UI组件

【免费下载链接】Instructions

Create walkthroughs and guided tours (coach marks) in a simple way, with Swift.

项目地址:https://gitcode.com/gh_mirrors/in/Instructions
点击查看免费下载

相关推荐

上一篇:conventionalcommits.org统计分析:全球开发者使用情况报告
下一篇:Formily Form 模型完全指南:@formily/core 核心表单模型 API 深入解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询