Highlight
Swift Testing 是今年推出的全新开源测试包,可以和 XCTest 在同一个 target 中共存,Xcode 16 新建测试目标时已默认选中它。
核心内容
写测试最烦的不是写测试本身,是 XCTest 的样板代码太多。每个测试必须继承 XCTestCase,方法名必须以 test 开头,断言要记一堆 XCTAssertXxx 的函数签名,setUp/tearDown 的生命周期还得小心翼翼。这些都不是”测试逻辑”,是框架的负担。
Swift Testing 的做法是把这些框架噪音降到最低。你只需要两样东西:@Test 标记函数,#expect 写断言。不需要继承任何类,不需要记住十几种断言函数,#expect 接受普通 Swift 表达式,失败时自动捕获子表达式的值。Xcode 16 中新建测试目标时,Swift Testing 已经是默认选项。
框架的四个构建块分别是:@Test 标记测试函数,#expect/#require 执行断言,Trait 定制测试行为,Suite 组织测试结构。每个测试函数在 Suite 中运行时都会创建独立的实例,避免了 XCTestCase 中常见的状态共享问题。整个框架采用开放开发流程,社区可以在 GitHub 上参与贡献和提案。
详细内容
写第一个测试
导入 Testing 模块,用 @Test 标记全局函数,用 #expect 断言(01:54):
import Testing
@testable import DestinationVideo
@Test func videoMetadata() {
let video = Video(fileName: "By the Lake.mov")
let expectedMetadata = Metadata(duration: .seconds(90))
#expect(video.metadata == expectedMetadata)
}
关键点:
import Testing引入框架,@testable import是 Swift 语言自身的特性,用于访问 internal 成员@Test可以标注全局函数,也可以标注类型中的方法,支持async、throws、全局 actor 隔离#expect接受普通表达式和运算符,失败时自动捕获源码和左右两边的值
#require:提前终止测试
#require 是 #expect 的严格版本,用于可选值解包或需要提前终止测试的场景(02:35):
@Test(.tags(.formatting)) func formattedDuration() async throws {
let videoLibrary = try await VideoLibrary()
let video = try #require(await videoLibrary.video(named: "By the Lake"))
#expect(video.formattedDuration == "0m 19s")
}
关键点:
#require需要加try,表达式为 false 或可选值为 nil 时抛出错误并终止测试- 适合后续代码依赖于前面结果的场景——值不存在时继续跑没有意义
Trait:定制测试行为
Trait 是第三个构建块,可以给测试添加描述信息、控制运行条件、修改行为(06:06):
@Test(.disabled("Due to a known crash"),
.bug("example.org/bugs/1234", "Program crashes at <symbol>"))
func example() {
// ...
}
关键点:
.disabled(...)禁用测试但保证代码仍能编译,比注释掉函数更安全.bug(...)关联问题追踪链接,在 Xcode 16 的 Test Report 中可直接点击打开.enabled(if:)控制运行条件,条件不满足时标记为 skipped.tags(...)给测试打标签,可跨文件、跨 target 分组,在 Test Navigator 中按标签查看和运行
Suite:组织测试结构
用 struct 包装测试函数,形成 Suite(07:18):
@Suite(.tags(.formatting))
struct MetadataPresentation {
let video = Video(fileName: "By the Lake.mov")
@Test func rating() async throws {
#expect(video.contentRating == "G")
}
@Test func formattedDuration() async throws {
let videoLibrary = try await VideoLibrary()
let video = try #require(await videoLibrary.video(named: "By the Lake"))
#expect(video.formattedDuration == "0m 19s")
}
}
关键点:
- Suite 鼓励用 struct,值语义避免状态共享;每个实例
@Test方法会创建独立的 Suite 实例 - 存储属性替代传统的
setUp,init替代setUp,deinit替代tearDown(deinit 需要用 class 或 actor) @Suite属性可以指定 display name 和 trait,子 Suite 和内部测试会继承
参数化测试
把重复的测试函数合并成一个参数化测试(14:07):
@Test("Number of mentioned continents", arguments: [
"A Beach",
"By the Lake",
"Camping in the Woods",
"The Rolling Hills",
"Ocean Breeze",
"Patagonia Lake",
"Scotland Coast",
"China Paddy Field",
])
func mentionedContinentCounts(videoName: String) async throws {
let videoLibrary = try await VideoLibrary()
let video = try #require(await videoLibrary.video(named: videoName))
#expect(!video.mentionedContinents.isEmpty)
#expect(video.mentionedContinents.count <= 3)
}
关键点:
- 函数签名加参数,
@Test的arguments:指定输入值 - Test Navigator 中每个参数值显示为独立的测试条目,可单独重跑和调试
- 比 for…in 循环好:每个参数的结果独立可见,支持并行执行,可以单独重跑失败的参数
与 XCTest 的关系
两者可以在同一个测试目标中共存(17:36)。UI 自动化(XCUIApplication)和性能测试(XCTMetric)仍需用 XCTest。迁移建议:多个结构相似的 XCTest 方法可合并为一个参数化测试;只有一个测试方法的 XCTestCase 可迁移为全局 @Test 函数。不要在 Swift Testing 测试中调用 XCTAssert 函数,也不要在 XCTestCase 中调用 #expect。
核心启发
-
用参数化测试消除重复的测试代码:项目中大量”同一逻辑、不同输入”的测试函数是维护负担。把它们合并成参数化测试,新增测试用例只需在 arguments 数组里加一行。怎么开始:找到项目中函数名靠后缀区分的测试(如
testVideoA、testVideoB),提取公共逻辑为参数化函数。 -
用 .tags 替代文件目录来组织测试:传统做法是按文件夹分组测试,但跨模块的关联很难表达。
.tags可以跨文件、跨 target 标记,在 Test Navigator 中按标签筛选和运行,还能在 Test Report 中看到同一标签下多个测试同时失败的关联洞察。怎么开始:为每个核心功能/子系统定义一个 Tag,逐步给现有测试打标。 -
用 .disabled + .bug 替代注释掉的测试:注释掉的测试不参与编译,代码可能早已腐坏。
.disabled保证代码始终能编译,.bug关联问题链接让禁用原因可追溯。怎么开始:搜索项目中所有注释掉的测试函数,替换为.disabled("原因").bug("链接")。 -
新项目直接用 Swift Testing,旧项目逐步引入:两个框架可以在同一个 target 中共存,不需要创建新 target。先在现有 XCTestCase 旁边添加新的
@Test函数体验#expect的诊断信息,等团队适应后再逐步迁移。怎么开始:在现有测试目标中 import Testing,写一个新的@Test函数跑通流程。
关联 Session
- Go further with Swift Testing — 深入讲解参数化测试的高级用法、标签与 trait 的更多细节
- Migrate a test from XCTest to Swift Testing — 逐步演示如何将现有 XCTest 测试迁移到 Swift Testing
- What’s new in Xcode 16 — Xcode 16 全新功能总览,包含 Swift Testing 的 IDE 集成
- Extend your Xcode Cloud workflows — Xcode Cloud 如何适配新的测试框架
评论
GitHub Issues · utterances