Swift Testing 还是 XCTest?2026 新手选择指南

Swift Testing 还是 XCTest?2026 新手选择指南

最后更新于 2026 年 9 月 18 日,版本信息核实自 Apple Developer、Swift.org 与 WWDC26 官方资料。

Xcode 27.2 beta 的官方系统要求显示,其编译器版本为 Swift 6.4,并要求 macOS Tahoe 26.6 或更高版本。这个版本信息直接给出选择结论:新建项目优先使用 Swift Testing 编写单元测试和集成测试,UI 测试继续使用 XCTest;旧课程或现有 XCTest 项目不必重写,可让两套框架共存后逐步迁移。 (developer.apple.com)

这篇指南适合三类人:

  • 第一次看到 @Test、#expect 和 XCTestCase,不知道该跟哪套教程的 Swift 初学者。
  • 正在完成旧课程或接手 XCTest 项目,担心更换框架导致作业失败的学生。
  • 只有 Windows、学校电脑或受限设备,需要判断哪些测试可以跨平台练习的人。

先分清测试任务:逻辑检查和真人操作检查

对学生来说,测试可以理解为提交作业前的自动检查。

如果要验证“输入两个数字后,函数是否返回正确结果”,这是逻辑检查。程序直接调用函数,然后比较结果。Swift Testing 更适合这一类任务。

如果要验证“应用启动后,用户能否点击按钮、输入文字并看到下一页”,这是真人操作检查。测试工具需要启动应用,再模拟用户动作。XCTest UI Tests 仍然承担这类工作。Apple 官方的新建项目流程也把 Swift Testing 单元测试与 XCTest UI Tests 作为组合选项,而不是要求学生二选一。 (developer.apple.com)

可以先记住下面的分工:

  • ✅ Swift Testing:函数逻辑、数据处理、异步业务逻辑、直接调用代码的集成测试。
  • ✅ XCTest:UI 自动化、性能测试、旧课程测试、已有 XCTestCase 测试类。
  • ⚠️ 不要因为 Swift Testing 是较新的框架,就把所有旧测试强行改写。

第一个最小测试怎么读

假设课程里有一个计算折扣后的价格函数:

import Testing

func finalPrice(_ price: Int, discount: Int) -> Int {
    price - discount
}

@Test
func calculatesFinalPrice() {
    let result = finalPrice(100, discount: 20)
    #expect(result == 80)
}

这里的 @Test 相当于告诉 Xcode:“请把这个普通 Swift 函数当作测试运行。”

#expect 则是判分条件。它检查 result == 80 是否成立。如果结果是 75,测试就会失败,并尽量显示参与比较的表达式。Apple 的 #expect 文档还说明,测试失败后默认可以继续执行后面的检查;如果某个条件不满足就必须停止,可以使用 #require。 (developer.apple.com)

对应的 XCTest 写法通常是:

import XCTest

final class PriceTests: XCTestCase {
    func testCalculatesFinalPrice() {
        let result = finalPrice(100, discount: 20)
        XCTAssertEqual(result, 80)
    }
}

两段代码都能检查同一个函数。区别在于,Swift Testing 更接近普通 Swift 函数;XCTest 则围绕 XCTestCase、测试方法和断言组织代码。 (developer.apple.com)

Swift Testing 还是 XCTest:按学习者情况选择

情况一:刚创建 SwiftUI 新项目

如果项目是刚创建的,且老师没有指定 XCTest,建议这样安排:

  1. 业务函数使用 Swift Testing。
  2. 数据模型和网络层的纯逻辑使用 Swift Testing。
  3. 需要点击页面、输入文本、验证跳转的部分使用 XCTest UI Tests。
  4. 提交前在 Xcode 的 Test navigator 中确认两类测试都能被发现。

Swift Testing 的优势不只是“更新”。官方资料明确提到,它支持参数化测试、异步测试、并发测试、标签和更灵活的测试组织。参数化测试适合用同一个规则检查多组输入,例如一次验证多个折扣组合,而不是复制多个几乎相同的测试函数。 (developer.apple.com)

例如:

import Testing

@Test(
    arguments: [
        (100, 20, 80),
        (50, 0, 50),
        (200, 50, 150)
    ]
)
func calculatesPrices(price: Int, discount: Int, expected: Int) {
    #expect(finalPrice(price, discount: discount) == expected)
}

输出结果可能会按不同参数拆成多个测试案例。对初学者来说,这比手写多个 XCTAssertEqual 更容易维护。

情况二:课程已经给出 XCTest 作业

如果课程已经要求创建 XCTestCase,或者老师提供的示例全部使用 XCTAssertEqual、XCTAssertTrue,最稳妥的选择是先保留 XCTest。

主要原因有三个:

  • 老师的评分脚本可能查找固定的测试目标或文件结构。
  • 旧项目中的辅助函数可能依赖 XCTest 的断言和生命周期方法。
  • 作业临近截止时,迁移框架会增加编译、测试计划和提交环境的不确定性。

Apple 官方明确说明:已有 XCTest 测试包不需要新建另一个测试包,学生可以直接在同一个测试目标中添加 Swift Testing 文件,并在时间允许时转换旧测试。 (developer.apple.com)

因此,“使用 Swift Testing”不等于“马上删除 XCTest”。对于课程项目,更合理的做法是:

  • 旧测试继续运行。
  • 新增的纯逻辑测试优先采用 Swift Testing。
  • 只有在作业要求允许、测试基线稳定后,再迁移高频修改的测试。
  • 每次迁移一小组,并立即重新运行全部测试。

情况三:接手小组项目或旧仓库

接手代码时,不要先根据个人偏好替换断言。先检查四项内容:

  1. 当前有哪些测试目标。
  2. 测试计划中包含哪些测试。
  3. 是否存在共享辅助函数。
  4. CI 或老师的验收命令是否依赖 XCTest。

这里的关键问题是互操作。Xcode 27 支持两套测试框架之间的互操作,但不同测试计划可能使用不同的处理模式。WWDC26 官方演示了 limited、complete、strict 和 none 等模式:在较宽松的模式下,某些来自另一框架的失败可能只显示为警告;在 complete 或 strict 模式下,问题更容易按错误处理。 (developer.apple.com)

这会造成一个很容易忽略的风险:

测试看起来通过
└── 实际存在跨框架断言问题
    └── Xcode 只显示警告

迁移前后应运行同一组测试,并比较失败数量和失败信息。不要只看最后的绿色图标。

⚠️ 经验提醒:课程提交前,至少保留一次“迁移前完整测试结果”。如果迁移后结果改变,学生才能判断是代码修复生效,还是测试配置发生了变化。

Swift 6.4 与跨平台练习的边界

Swift 6.4 已经包含 Swift Testing 与 XCTest 的互操作能力。Swift.org 的发布说明显示,测试代码可以在 Swift Testing 测试中使用 XCTAssert,也可以在 XCTest 中使用 #expect。这为渐进迁移提供了技术基础,但不代表所有旧 API 都应该混用。 (swift.org)

没有 Mac 的学生可以先练习以下内容:

  • 纯 Swift 函数。
  • 数据结构和字符串处理。
  • Swift Package。
  • 不依赖 UIKit 或 SwiftUI 的业务逻辑。
  • Swift Testing 的 @Test、#expect 和参数化测试。

Swift 官方资料列出了 Swift Testing 的跨平台支持,包括 Apple 平台、Linux 和 Windows。也就是说,纯 Swift 测试可以先在非 Mac 环境完成一部分练习。 (swift.org)

但以下任务不能简单等同于跨平台 Swift 练习:

  • 创建和运行 Xcode 项目。
  • 启动 iOS 模拟器。
  • 验证 SwiftUI 页面。
  • 运行 XCTest UI Tests。
  • 检查 iOS 项目的测试计划和 .xcresults 结果包。

Apple 的测试运行文档说明,使用 xcodebuild test 可以生成包含测试结果、日志和覆盖率信息的 .xcresults 文件。课程如果要求提交 Xcode 测试结果,非 Mac 环境通常无法完整替代这一步。 (developer.apple.com)

新手决策条件:满足哪一条就选哪一套

可以按下面的分支快速判断:

  • 若是新建项目,且测试直接调用 Swift 函数,选择 Swift Testing。
  • 若是新建项目,且测试需要点击、输入或验证页面跳转,选择 XCTest UI Tests。
  • 若课程明确要求 XCTestCase 或 XCTest 断言,保留 XCTest,不为追新而改写。
  • 若旧项目已经通过测试,只是想增加新逻辑测试,让 Swift Testing 与 XCTest 共存。
  • 若作业临近截止、当前测试还没有完整通过,暂停迁移,先保证现有环境可复现。
  • 若只有 Windows,但当前只练习纯 Swift 逻辑,可以先使用 Swift Testing。
  • 若任务进入 SwiftUI、模拟器或 UI 自动化,切换到学校 Mac、借用设备或远程真实 Mac。

这套判断的核心不是“哪个框架绝对更好”,而是测试对象、课程要求和交付环境是否匹配。

FAQ:学生最容易卡住的 5 个选择

新项目应该从哪套框架开始?

新项目中的纯逻辑测试优先从 Swift Testing 开始。它使用 @Test 和 #expect,对刚接触 Swift 的学生更像普通函数。页面操作测试则直接建立 XCTest UI Tests,避免后面为了补 UI 流程而重新搭建测试目标。

已有 XCTest 作业是否必须全部迁移?

不必。若老师没有要求 Swift Testing,已通过的 XCTest 作业应保持稳定。可以把新增的业务逻辑测试写成 Swift Testing,但每次修改后都要运行旧测试和新测试,确认提交环境仍能发现全部测试。

Swift Testing 能否替代 UI 测试?

不能完全替代。Swift Testing 适合检查输入、输出、异步逻辑和数据变化;UI 测试需要启动应用并操作界面,仍应使用 XCTest。一个完整的 SwiftUI 作业经常同时包含两套测试。

两套框架共存时最容易出什么问题?

最常见的问题不是代码无法编译,而是测试计划的互操作模式不一致。跨框架断言可能以警告显示,导致学生误以为测试完全通过。迁移时应查看 Test navigator 和测试报告,而不是只看总状态。

没有 Mac 时可以完成多少练习?

没有 Mac 时,可以完成纯 Swift 函数、Swift Package 和部分 Swift Testing 练习。涉及 Xcode 工程、SwiftUI、iOS 模拟器、UI 自动化或课程要求的 Xcode 结果文件时,仍需要真实 Mac 环境。

提交前的双框架验收清单

完成作业前,建议按以下顺序验收:

  1. 确认测试目标能够被 Xcode 识别。
  2. 分别运行 Swift Testing 和 XCTest 测试。
  3. 人为制造一次错误,确认失败状态确实出现。
  4. 修复错误,再确认测试恢复通过。
  5. 检查 UI 测试是否真的启动应用并完成点击流程。
  6. 打开测试报告,查看失败信息而不是只看总数。
  7. 如果使用了旧课程仓库,确认老师要求的文件名、测试类和命令没有被改变。
  8. 如果在 Windows 上练习过纯 Swift,最后仍要在目标 Mac 环境重新运行一次。

如果只有一台受限电脑,最实际的路径是先在本地完成 Swift 逻辑,再把项目同步到可运行 Xcode 的 Mac 上做最终验收。想了解没有 Mac 时如何安排 Swift 与 iOS 学习步骤,可以参考 没有 Mac 学 Swift 与 iOS 开发的学习路线;如果需要核对远程 Mac 的使用方式,也可以先查看 Mac 远程租赁方案与价格。

对于短期课程项目,当前设备方案通常有三个真实限制:学校电脑可能没有 Xcode 权限,Windows 无法直接运行 iOS 模拟器,临时借用设备又难以保证项目文件、测试结果和版本环境连续。若课程只剩一次验收,租用一台可远程连接的真实 Mac,往往比为了一个测试作业临时购买整机更容易控制成本与时间。SFTPMAC 的 远程 Mac 使用入口 更适合先完成 Xcode、SwiftUI 和双框架测试验证;但如果需要长期高强度开发、连接实体 iPhone 调试,或必须拥有本地物理接口,购买自己的 Mac 仍然更合适。