- 测试
- 开发工具
【免费下载链接】Quick
The Swift (and Objective-C) testing framework.
本篇指南围绕 Quick 测试框架的使用前置环节——在 Xcode 工程中正确搭建测试 Target 与跨语言测试桥接,系统讲解 Swift 与 Objective-C 四种组合(Swift 测 Swift、Swift 测 Objective-C、Objective-C 测 Swift、Objective-C 测 Objective-C)的配置步骤,并补充命令行工具项目测试 Target 的创建流程,以及 Quick 在 XCTest 底层如何被发现的源码级原理。读完本文,你将能在自己的 Xcode 项目中稳定接入 Quick 并让测试 Target 正常访问主 Target 的代码。
本文基于当前仓库 SettingUpYourXcodeProject.md 编写,源码证据来自 Sources 目录与 Quick.xcodeproj/project.pbxproj。在开始之前,建议先阅读 安装 Quick 的三种方式(Git Submodules / CocoaPods / Swift Package Manager),并对照 README 中的 Swift 版本兼容表 选择与你的 Swift 版本匹配的 Quick、Nimble 版本。
前置认知:Xcode 默认的测试 Target 与访问主 Target 代码的前提
除 Command Line Tool(命令行工具)项目类型外,使用 Xcode 7 及以上版本新建项目时,工程中会默认包含一个单元测试 Target。对于命令行工具项目,需要手工创建,具体步骤见下文「为命令行工具项目设置测试 Target」。
要编写单元测试,核心前提是:测试 Target 能够使用主 Target 的代码。Xcode 默认生成的测试 Target 只是一个独立的 bundle,它不会自动获得主模块的内部符号可见性,需要按下面四种语言组合分别配置。
用 Swift 测试 Swift 代码
这是最常见的组合。需要完成两件事:
1. 将主 Target 的 "Defines Module" 设置为 YES
在 Xcode 中操作:
- 选中你的项目(Project);
- 进入 "Build Settings";
- 找到 "Packaging" 分组下的 "Defines Module";
- 将其值改为 "Yes"。
注意:如果你的 Build Settings 显示为 "Basic"(基本),可能看不到 "Packaging" 分组,需要先切换为 "All"(全部)再查找。
这一设置的意义在于:它让 Xcode 为你的主 Target 生成一个 module(模块),后续@testable import才能按模块名解析到主 Target 的代码。这也是 Swift 语言层面模块化的基本要求。
2. 在测试文件中使用@testable import导入主模块
在测试代码中,通过@testable import导入你的应用模块名。这会把主模块中所有public与internal(默认访问级别)符号暴露给测试代码,而private符号仍然不可访问。
// MyAppTests.swift import XCTest @testable import MyModule class MyClassTests: XCTestCase { // ... }为什么是@testable而不是普通import
普通import只能看到public级别的符号;而单元测试经常需要验证模块内部实现细节(例如内部结构体、内部方法),@testable import正是 Apple 为测试场景提供的访问通道。注意其使用前提就是上文第 1 步的 "Defines Module = YES",两者缺一不可。
结合源码理解:Quick 如何借助 XCTest 发现测试
配置好测试 Target 后,Quick 的 Spec 会被 XCTest 自动发现并执行。其机制可以在仓库源码中看到:
- QuickSpec.h 的注释说明了完整流程:QuickSpec 是
XCTestCase的子类,当 Spec 类首次加载时,+[NSObject initialize]被调用,QuickSpec 重写该方法来执行spec(),从而构建 example group 并把示例注册到Quick.World这个全局注册表; - 随后 XCTest 会查询测试方法列表。QuickSpec.m 重写了
+[XCTestCase testInvocations],遍历World中为该 Spec 类注册的每个 example,动态为类添加实例方法,并返回对应的NSInvocation。这些 invocation 就是最终被 XCTest 执行的测试,其选择器名称会显示在 Xcode 的测试导航栏中; - 在单条测试单独运行时(点击导航栏中的单个测试),XCTest 不会隐式调用
defaultTestSuite,因此 QuickSpec.m 还重写了instancesRespondToSelector:,显式触发defaultTestSuite来保证 example 仍会被正确生成; - 在 Swift Package Manager 场景下,QuickSpec.swift 通过重写
defaultTestSuite钩子,调用QuickConfiguration.configureSubclassesIfNeeded完成全局配置并收集每个 Spec 类的 examples,效果等同于 Linux 上在LinuxMain.swift中显式罗列 Spec 类。
也就是说:只要测试 Target 正确链接了 Quick.framework(或通过 SwiftPM 添加了 Quick 依赖),你的QuickSpec子类就会被 XCTest 运行时自动发现,无需手动注册。
用 Swift 测试 Objective-C 代码
如果你要测试的代码是用 Objective-C 编写的,需要让 Swift 测试文件能"看到"对应的头文件:
- 为测试 Target 添加一个桥接头文件(Bridging Header);
- 在该桥接头文件中
#import你要测试的代码所在的头文件。
// MyAppTests-BridgingHeader.h #import "MyClass.h"配置完成后,就可以在 Swift 测试文件中直接使用MyClass.h中声明的代码了。
桥接头文件的工程配置
在 Xcode 中,桥接头文件的路径通过 Build Settings 中的SWIFT_OBJC_BRIDGING_HEADER指定。仓库自身的测试工程就是一个真实范例:Quick.xcodeproj/project.pbxproj 中配置了:
SWIFT_OBJC_BRIDGING_HEADER = Tests/QuickTests/QuickTests/Helpers/QuickTestsBridgingHeader.h;该桥接头文件位于 Tests/QuickTests/QuickTests/Helpers/QuickTestsBridgingHeader.h。Quick 自身同时维护 Swift 与 Objective-C 两套测试(见 Tests/QuickTests/QuickTests/FunctionalTests/ObjC 下的*Tests+ObjC.m文件,例如 ItTests+ObjC.m),正是依靠这个桥接头文件在同一个测试 Target 内打通两种语言。你可以照此模式为自己的测试 Target 添加桥接头文件。
用 Objective-C 测试 Swift 代码
Objective-C 无法直接看到 Swift 符号,需要两个步骤:
- 用
@objc属性把你希望测试的 Swift 类与方法暴露给 Objective-C; - 在单元测试中导入模块的 Swift 头文件(即 Xcode 自动生成的
<模块名>-Swift.h)。
@import XCTest; #import "MyModule-Swift.h" @interface MyClassTests: XCTestCase // ... @end这里MyModule-Swift.h是 Xcode 在构建时为主 Target 自动生成的头文件,它汇总了所有被@objc暴露的 Swift 声明。注意:Swift 中仅有@objc标记(且继承自NSObject或符合 ObjC 兼容条件的类型)的符号才会出现在这个头文件中,纯 Swift 泛型、private等声明不会出现。
关于模块名的生成规则,仓库中的 NSBundle+CurrentTestBundle.swift 给出了参考实现:模块名取自 bundle 文件名,并转换为合法的 "C99 extended identifier"(对应实现见 String+C99ExtendedIdentifier.swift)。这解释了为什么-Swift.h前缀要使用符合 C 标识符规则的模块名。
用 Objective-C 测试 Objective-C 代码
这是最简单的组合:直接在测试文件中导入被测代码的头文件即可。
// MyAppTests.m @import XCTest; #import "MyClass.h" @interface MyClassTests: XCTestCase // ... @end无需@testable(Objective-C 没有访问级别隔离到模块内部符号的机制),也无需桥接头文件(同为 Objective-C,编译单元直接可见头文件声明)。
为命令行工具项目设置测试 Target
命令行工具项目默认不含测试 Target,按以下步骤创建:
- 在项目导航(project pane)中为你的工程添加一个 Target;
- 选择 "OS X Unit Testing Bundle" 模板;
- 编辑主 Target 的 Scheme;
- 选择 "Test" 节点,点击 "Info" 标题下方的 "+" 按钮;
- 在弹出的列表中选择你的测试 bundle。
完成这 5 步后,命令行工具的主 Target 代码即可通过前文所述方式(Swift 场景需先设置 Defines Module 并配合@testable import)被测试 Target 访问。需要注意的是,命令行工具的测试同样遵循上述四种语言组合的配置规则。
常见误用与已知限制
不要直接把源码文件加入测试 Target
一些开发者倾向于把 Swift 源文件直接拖入测试 Target 来"共享代码"。这不是推荐做法:它会导致难以诊断的隐蔽错误(详见 Quick 仓库 issue #91 的讨论),例如同一类型在编译单元间产生符号重复、初始化顺序异常等问题。正确做法始终是使用@testable import(Swift)或桥接头文件 /-Swift.h(跨语言场景)。
Quick 在 Xcode Test Navigator 中的集成限制
Quick 与 Xcode Test Navigator(测试导航器)的集成存在一些已知局限:
- Quick 测试在运行过之后才会出现在测试导航器中;
- 重复运行往往会以不可预期的方式重置测试列表;
- 无法从源码编辑器左侧的 gutter(行槽)直接运行单个 Quick 测试。
如果你希望 Apple 工程师改进这一体验,可以向 Apple 提交 radar,并标注为 rdar://26152293 的重复(duplicate)问题以提升权重。这一限制是 XCTest 运行时动态注入测试方法(上文testInvocations机制)的固有表现:在运行之前,XCTest 并不知道 Spec 中有哪些 example。
隐私与发布注意
Quick 仅用于测试,不应被打包进提交到 App Store Connect 的二进制中(详见 README 的隐私声明);它依赖私有 API 与 Xcode 集成,若被打包会导致应用被拒审。
非 Apple 平台与 SwiftPM 场景下的补充
以上配置均针对 macOS / iOS / tvOS 上的 Xcode 工程。如果你通过 Swift Package Manager 在 Linux 等非 Apple 平台使用 Quick,测试的入口与 Xcode 场景不同:由于 swift-corelibs-xctest 不支持自动发现 Spec 类,需要在Tests/LinuxMain.swift中创建入口并调用QCKMain(实现见 QuickMain.swift),显式传入QuickSpec子类列表、可选的QuickConfiguration子类列表与额外的XCTestCase列表。在当前仓库的 Tests/LinuxMain.swift 中可以看到实际用法。
而在 Apple 平台上使用 SwiftPM 时,则不需要手写LinuxMain.swift:如 QuickSpec.swift 注释所述,XCTest 的自动发现机制由 Objective-C runtime 驱动,defaultTestSuite钩子即可完成配置与 example 收集。当前仓库 Package.swift 声明的最低平台为 macOS 10.15 / iOS 13 / tvOS 13,可作为你在 Apple 平台使用 SwiftPM 集成时的参考基线。
小结:配置检查清单
| 场景 | 核心配置 | 测试代码中的关键语句 |
|---|---|---|
| Swift 测 Swift | 主 Target 设Defines Module = YES | @testable import MyModule |
| Swift 测 Objective-C | 测试 Target 配置桥接头文件(SWIFT_OBJC_BRIDGING_HEADER) | 桥接头文件中#import "MyClass.h" |
| Objective-C 测 Swift | 被测 Swift 符号加@objc | #import "MyModule-Swift.h" |
| Objective-C 测 Objective-C | 无额外配置 | #import "MyClass.h" |
| 命令行工具项目 | 新建 "OS X Unit Testing Bundle" Target,并在主 Target Scheme 的 Test 节点中添加 | 同以上四种组合 |
按照上述清单完成配置后,即可在测试 Target 中import Quick,参照 QuickExamplesAndGroups.md 编写describe/context/it风格的 BDD 测试,并结合 NimbleAssertions.md 使用expect(...).to断言语法。
- 测试
- 开发工具
【免费下载链接】Quick
The Swift (and Objective-C) testing framework.
相关推荐
在 Xcode 项目中配置 Quick 测试环境:Swift 与 Objective-C 四种测试组合的 Target 搭建指南
在 Xcode 项目中配置 Quick 测试环境:Swift 与 Objective C 四种测试组合的 Target 搭建指南 本篇指南以 Quick 官方文
测试开发工具Quick 文件模板安装指南:为 Xcode 配置 Swift 与 Objective-C 测试模板
Quick 文件模板安装指南:为 Xcode 配置 Swift 与 Objective C 测试模板 Quick 仓库自带一套面向 Swift 与 Object
测试开发工具在 Xcode 项目中配置 Quick 单元测试:四种语言组合与测试 target 搭建完整指南
在 Xcode 项目中配置 Quick 单元测试:四种语言组合与测试 target 搭建完整指南 Quick 是运行在 XCTest 之上的行为驱动开发(BDD
测试开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考