新单元测试优先采用 Swift Testing,存量 XCTest 按风险分批迁移;UI 自动化等仍依赖 XCTest 的部分继续保留。Swift Testing 迁移不等于删除 XCTest:先验证新旧框架互操作,再按项目需求收紧检查模式。
这篇判断适合维护 XCTest 单元测试、又想控制迁移风险的独立开发者;也适合担心辅助断言行为变化、需要本地或 CI 稳定复现的 iOS 与 macOS 小团队。
[ SECTION_01 ] Swift Testing 迁移先看覆盖边界,而不是测试文件数量
先按测试职责分类。纯 Swift 的业务逻辑、值类型转换、输入输出规则,通常更容易作为迁移候选;它们不需要启动 App,也不依赖 XCTest 专有的 UI 或性能能力。相比之下,套件里只要混有 UI 流程、性能基线或 Objective-C 异常测试,就不适合整组替换。
Apple 将 Swift Testing 定位为可用于 Swift Package 和 Xcode 项目的测试库,并提供参数化测试、测试分组及并行执行等能力;这并不意味着 XCTest 的所有用途都已被覆盖。Swift Testing 文档 与 XCTest 文档列出的职责范围并不相同。
| 决策选项 | 覆盖与互操作 | CI 与维护风险 | 建议优先级 |
|---|---|---|---|
| 新写纯 Swift 单元测试 | 用 @Test 和 #expect 表达;需要时可共享测试辅助代码 |
先验证测试发现、结果收集和辅助断言 | ★★★★★,优先采用 |
| 存量纯逻辑测试分批迁移 | 与 XCTest 同项目运行;交叉调用要核对失败语义 | 改一小批,再跑本地与 CI 回归 | ★★★★☆,验证后推进 |
| UI 自动化、性能或 Objective-C 异常测试 | 继续使用现有 XCTest 能力 | 贸然迁移可能丢失原有测试能力或测量方式 | ★★★★★,明确保留 |
| 整个测试目标一次替换 | 容易同时改变断言、辅助函数和执行方式 | 故障来源难以分辨,回滚范围也大 | ★☆☆☆☆,通常不建议 |
表中的星级是迁移决策建议,不是性能评分。Apple 明确指出,UI 自动化与性能测试 API 仍属于 XCTest 的适用场景;测试 Objective-C 异常时,也应保留以 Objective-C 编写的 XCTest。XCUIAutomation 文档说明其通过 XCTest 操作和检查 App 界面;性能测试文档介绍 XCTest 的性能测量能力。
[ SECTION_02 ] 互操作验收要检查失败如何进入结果
“新旧测试能在同一项目里运行”只是第一关。真正要核对的是:跨框架调用的断言失败后,Xcode 测试报告和 CI 是否把它识别成预期级别的问题。
Apple 的迁移文档说明,单个源文件可以同时包含 XCTest 与 Swift Testing 测试,二者也能通过互操作共享辅助函数。该文档还列出 none、limited、complete、strict 四种互操作模式:例如,none 不报告跨框架问题;complete 保留 XCTest 断言失败的严重性;strict 则会将部分跨框架问题升级为致命错误。具体默认值取决于工具链和 Swift Package 的 swift-tools-version,所以不要假定不同机器配置天然一致。迁移文档中的互操作说明
尤其要审查旧辅助函数。一个函数表面上只是校验数组唯一性,内部却可能调用 XCTFail。如果它在 Swift Testing 测试中触发,团队必须确认测试结果里确实出现可识别的失败,而不是只看到警告,甚至完全没有失败记录。反向调用也要测:由 XCTest 测试调用 Swift Testing 的断言或期望时,结果是否进入当前测试的报告。
第一轮可以使用适合混合运行的模式收集问题。确认旧断言、失败位置和 CI 状态都符合预期后,再评估提高严格程度。不要把“关闭互操作问题报告”当作修复;那只是隐藏信号,不会证明断言语义已经兼容。
[ SECTION_03 ] 并行安全取决于隔离,不取决于框架新旧
Swift Testing 默认并行运行测试;参数化测试的各个用例也可能并行执行。Apple 的并行测试说明提到,可通过 .serialized 等方式控制执行方式。并行本身不是迁移收益保证:如果测试争用同一个临时文件、共享数据库、固定端口或可变全局状态,切换框架后可能更容易暴露顺序依赖。
迁移前逐项排查:
- 共享状态:单例、全局缓存、进程级环境变量是否会被多个测试同时修改?
- 文件与网络资源:临时目录、固定文件名、测试服务器端口是否会冲突?
- 顺序依赖:某个测试是否依靠前一个测试创建数据、清理状态或设置配置?
- 隔离方式:是否能为每个用例生成独立资源,或者对必须串行的测试显式限制并行?
参数化测试适合把一组输入转成可单独识别的测试用例;但如果每个用例都改写同一份文件或远端记录,就应先隔离资源,再考虑并行。参数化测试文档说明其用法及用例执行特征。对于需要验证进程退出行为的测试,Swift Testing 也提供单独的退出测试机制;应确认它和现有测试目标、运行环境相符,而不是仅因 API 更新就迁移。退出测试文档
[ SECTION_04 ] UI 与特殊测试能力要单独分流
框架迁移不应覆盖所有测试类型。凡是验证真实界面操作、性能基线或 Objective-C 异常的测试,都先留在 XCTest;若一个测试目标同时承担多种职责,可以按测试类或文件逐步划分,而不是为了整齐把测试整体改写。
新能力应以缺口为理由采用。例如,重复输入的逻辑测试如果需要更清楚地显示哪组输入失败,可以试用参数化测试;需要检查特定进程退出路径时,可以验证退出测试是否适配现有工作流。每个新能力都要用项目里的真实用例检查结果,不能把“新框架支持”直接等同于“项目已经受益”。
[ SECTION_05 ] 本地通过不够:把 Package、Xcode 和 CI 放在同一条验收链上
Xcode 27 的迁移讨论值得纳入评估,但需要区分工具链事实与项目结论。Apple 的 Xcode 27 发布候选版本说明列出 Swift 6.4,并说明测试计划可配置 Swift Testing 与 XCTest 互操作行为。实际项目仍要以本地安装版本、Package 工具版本和 CI 使用的 Xcode 为准,不能只看版本名称推断配置一致。
按以下步骤做一次可复现验收:
- 建立测试清单:记录测试属于纯逻辑、UI 自动化、性能测量还是特殊失败场景,并注明所依赖的辅助函数与资源。
- 记录基线结果:用当前 XCTest 配置运行目标测试,保存失败摘要、测试结果文件和 CI 状态,避免迁移后没有可比参照。
- 挑选低耦合样本:选少量不依赖界面、共享状态或特殊 XCTest API 的纯 Swift 测试,转换后单独执行,再执行完整测试目标。
- 故意验证跨框架失败:让旧辅助函数在受控用例中触发失败,检查本地报告、命令行结果和 CI 是否都按预期标记失败;随后移除临时用例。
- 统一工具链与模式:核对开发机、CI 和 Swift Package 使用的工具链与互操作模式。Swift Package Manager 项目应特别检查
swift-tools-version,避免不同环境意外采用不同默认值。 - 观察并发与复跑结果:对共享文件、网络服务和顺序敏感测试做重复运行;记录失败是否可重现、是否能定位到具体测试。出现资源冲突就先修隔离,必要时仅对特定测试串行执行。
- 分批合并并设置回退点:每批只处理明确的一类测试。若测试发现、失败语义或结果收集变化,就暂停后续迁移,先恢复可比较状态。
对使用 Swift Package Manager 的项目,官方文档明确支持 Swift Testing 的 Package 测试工作流;但实际验收仍应在团队使用的工具链和 CI 命令中完成。Swift Testing 官方概览说明其与 Swift Package Manager 的集成。每一批变更都应保留运行命令、互操作配置和测试结果,不能只凭开发者电脑上一次通过就宣布迁移完成。
[ SECTION_06 ] 用可复现证据决定是否继续扩大迁移
迁移决策可以落在三类结果上:
- 继续迁移:候选测试是纯 Swift 逻辑;新旧断言失败都被正确识别;本地和 CI 的测试发现及结果收集一致;并发运行没有引入资源冲突。
- 保持双轨:单元测试可以逐批迁移,但仍有 UI 自动化、性能测量或特殊异常测试需要 XCTest。保留旧框架是能力边界,不代表迁移失败。
- 暂停并修复:断言只显示警告、辅助函数没有让测试失败、不同环境结果不一致,或并行运行出现不可复现的资源冲突。先统一配置并消除测试依赖,再评估下一批。
如果当前测试只在一台本地 Mac 上运行,短期继续本地执行通常最直接;若已有稳定 CI Runner,也不必因为迁移就额外增加执行环境。相反,测试需要长期回归、团队需要隔离的 macOS 工具链,或成员无法稳定访问本机 Mac 时,租用远程 Mac 可以减少自购设备与持续维护 Runner 的负担。远程环境不会自动解决互操作和测试隔离问题,仍需使用同一套测试命令、收集结果文件并复现失败。需要评估可用 Mac 方案时,可查看 NOVAKVM 的 Mac 选项,并从 NOVAKVM 首页了解远程 Mac 服务;若当前方案是固定本机,则它虽然控制简单,但会占用本地资源、难以为团队共享,也不天然提供持续在线的独立测试节点。
常见问题
Swift Testing 和 XCTest 能放在同一个项目里吗?
可以。Apple 的迁移说明允许在同一文件混写,但文件需要同时导入 Testing 与 XCTest;也可以分开放在同一测试目标。关键不是能否编译,而是交叉调用断言时,失败能不能按预期报告。先在测试计划或 Swift Package 配置中明确互操作模式,再用故意失败的测试验证结果,避免仅凭一次绿色构建判断兼容。
哪些 XCTest 测试不该急着迁移?
依赖 XCUIAutomation 的 UI 自动化、使用 XCTest 性能测试 API 的基准测试,以及检查 Objective-C 异常的相关测试,应先留在 XCTest;最后一种场景还涉及测试代码的语言边界。不要只按文件或测试目标整体搬迁,先标记这些能力依赖,再把纯 Swift 逻辑测试单独作为候选。新框架的参数化或退出测试能力,只有覆盖了真实缺口才构成迁移理由。
怎么证明 XCTest 辅助断言在 Swift Testing 里失败时会让测试失败?
给辅助函数准备一个受控的失败用例,例如让它调用 XCTFail,然后在 Swift Testing 测试中触发它。运行后检查测试结果和 CI 退出状态,确认失败不是只显示为提醒,也没有被测试计划或互操作配置降级。再以相反方向验证 Swift Testing 断言是否能被 XCTest 测试识别。测试结束后移除临时失败用例,并保留结果记录作为迁移验收证据。
Swift Package Manager 项目怎样分步引入 Swift Testing?
先确认 Package.swift 中的 swift-tools-version、执行测试的 Swift 工具链,以及 CI 调用的 swift test 版本;互操作默认行为会受工具链和 package tools 版本影响。之后在现有测试目标中增加少量 Swift Testing 测试,分别运行新旧测试,并验证辅助断言失败、测试发现和结果汇总。若本地与 CI 的互操作模式不同,应显式统一配置,而不是单独升级 tools version 来碰运气。