概述
从 WWDC 24 开始,苹果推出了全新的测试机制:Swift Testing。利用它我们可以大幅度简化之前“老态龙钟”的 XCTest 编码范式,并且使得单元测试更加灵动自由,更符合 Swift 语言的优雅品味。
在这里我们会和大家一起初涉并领略 Swift Testing 的测试之美。
在本篇博文中,您将学到如下内容:
- 概述
- 1. 何为 Swift Testing?它和之前的 XCTest 有何不同?
- 2. 如何将 Swift Testing 集成到 Xcode 项目中?
- 3. 御驾亲征:用鲜活的例子说话
- 总结
测试为先,质量为王!无测试,不软件!
那还等什么呢?Let’s testing!!!😉
1. 何为 Swift Testing?它和之前的 XCTest 有何不同?
Swift 测试(Swift Testing)是今年 WWDC 24 中苹果为 Swift 撸码增加的一项重磅功能,它充分利用去年 WWDC 23 推出的宏(Macro)机制,并借助 Swift 语言的“天赋异禀”迅速成为苹果代码开发中的心膂股肱。
在过去,为了完成项目中的单元测试,我们需要借助 XCTest 测试“套件”的力量,可以看到它诞生于远古的 Xcode 7.2:
使用 XCTest 框架,我们可以这样写单元测试:
import XCTest
final class ExampleXCTest: XCTestCase {
// 构造操作
override func setUpWithError() throws {
}
// 析构操作
override func tearDownWithError() throws {
}
func testExample() throws {
XCTAssertTrue(true, "该测试会永远通过!")
}
}
从上面代码可以看到,我们需要继承 XCTestCase 类实现所需的测试方法集。其中需要设置好必要的测试构造和析构方法,并且所有的测试方法都必须以 “test” 开头。
使用“年代久远”的 XCTest 进行测试似乎有些繁文缛礼、连篇累牍,况且多如牛毛的 XCTAssertXXX 重载方法的选择也会让秃头码农们目不暇接。而使用 Swift Testing 我们可以大大简化 XCTest 的构造过程。
比如,下面是用 Swift Testing 重写的测试代码:
import Testing
@Test func swiftTestingExample() {
// 构造操作
#expect(true, "该测试会永远通过!")
// 析构操作
}
从新测试代码可以看到,它们与 XCTest 有以下几点不同:
- 不需要先创建派生自 XCTestCase 的测试类;
- 直接写测试逻辑,简单明了、一发入魂;
- 测试方法名无需限定于特定前缀,而是用 @Test 宏来修饰;
Swift Testing 除了能够简化测试逻辑以外,我们还可以利用它方便的测试“重复”的条件以及灵活的将多个情景相同的测试“聚合成组”。
更多 Swift Testing 的使用奥秘,请小伙伴们移步苹果官方站点观赏 WWDC 24 开发视频:
- ▶️ Meet Swift Testing
- ▶️ Go further with Swift Testing
2. 如何将 Swift Testing 集成到 Xcode 项目中?
使用 Swift Testing 进行单元测试主要有两种方式。
一种是在创建新项目时,就选择将它加入到 Testing System 中去:
否则,要想将 Swift Testing 加入已存在的 Xcode 项目里,我们不能只通过添加一个包含测试逻辑的 swift 文件来达到目的,那样做的话会让 Xcode “大声抱怨”找不到 Testing 框架:
至少在 Xcode 16 中暂时无法这样做。我猜测是因为使用 Swift Testing 进行测试并不仅仅是单纯导入了 Testing 框架,Xcode 还需要修改项目的配置信息来深度集成测试环境。
作为替代,我们必须为项目新增一个 Unit Testing 编译目标(Target),并选择 Swift Testing 作为测试系统:
3. 御驾亲征:用鲜活的例子说话
为了能让小伙伴们更深刻的领悟到 Swift Testing 的“魅力”,拒绝冷冰冰的说教,我们决定写一个实际的“栗子”来“融会贯通”。
首先,创建一个 Xcode 项目并按照之前的方法加入 Swift Testing 测试系统。
接着,我们新建数据模型:
import Foundation
fileprivate let fakeNames = ["黄飞鸿", "齐天大圣", "黑神话悟空"]
struct Item {
let name: String
let age: Int
let power: Double
let isImmortal: Bool
static func createTestItems() -> [Item] {
fakeNames.map {
Item(
name: $0,
age: Int.random(
in: 1...100
),
power: Double.random(
in: 5...1000
),
isImmortal: Bool.random()
)
}
}
}
struct Model {
static var shared = Model()
private(set) var items = [Item]()
mutating
func createItems() {
items = Item.createTestItems()
}
mutating
func deleteAllItems() {
items.removeAll()
}
}
在上面的代码中我们创建了 Item 类型,并将其包裹到 Model 数据模型中以备后续使用。
随后,我们编写测试逻辑:
import Testing
@testable import SwiftTestDEMO
@Test("测试创建 Itmes")
func createItems() {
var model = Model.shared
model.createItems()
#expect(!model.items.isEmpty, "应该成功创建若干 Items!")
}
在上面的代码中,我们使用 createItems 方法验证了创建 Item 是否真正成功。可以看到:我们的 createItems 方法“一枝独秀”,摆脱了所有不必要的桎梏,显得那么无拘无束。
按住 Command(⌘)+ U 快捷键,我们第一个测试必须顺利通过:
在后面的博文中,我们将继续介绍 Switch Testing 中一些重要宏的应用场景,敬请期待吧!
想要系统学习 Swift 的小伙伴们,请来我的《Swift语言开发精讲》专栏逛一逛哦:
- 《Swift 语言开发精讲》
总结
在本篇博文中,我们介绍了 WWDC 24(Xcode 16)新引入的 Swift Testing 测试系统、比较了它和 XCTest 的区别、如何集成到 Xcode 项目中,并用一个鲜活的例子讨论了它的金辉玉洁。
感谢观赏,下篇再会啦!😎