背景
苹果在 WWDC 2024 上宣布了 Swift Testing 框架。它改变了我们在 Swift 中编写测试的方式。一个新的清晰、富有表现力的 API 使得编写测试更加直接,而 Xcode 用户界面在测试失败或成功时与改进的反馈进行通信。
测试框架包含了Swift宏,减少你必须为重复测试编写的样板代码。所谓的参数化测试可帮助你在一系列值上运行类似的测试。因此,你不再需要为不同的输入值重复和维护几乎相同的测试。让我们了解下 Swift testing 是如何工作的,以及如何使用它。
使用
如果你是测试新手,则没有太多可比较的。但是,如果你习惯了 XCTest,那么在使用 Swift testing 框架编写测试时,会有很多事情发生变化。
首先,我们需要导入一个不同的框架:
import Testing
导入之后,我们可以开始定义我们的第一个测试。有趣的是,我们可以全局定义测试:
struct Person {
var firstName: String
var lastName: String
var fullName: String {
get {
return self.firstName + self.lastName
}
}
}
import Testing
@testable import SwiftTestingPlayground
@Test func personFullName() {
let person = Person(firstName: "Jack", lastName: "Chen")
#expect(person.fullName == "Jack Chen")
}
注意,我们正在使用 @testable
属性导入 SwiftTestingPlayground,以允许访问内部类型,比如 Person 结构体。
我们使用一个新的 @Test 宏定义了测试,它取代了 XCTest 测试方法前缀。在测试中,我们使用了 #expect 宏,它取代了 XCAssert 之类的断言。一定要忘记把test写成前缀的旧习惯。
虽然全局测试在这样的小项目中工作得很好,但我们可能需要寻找一种更好的方法来组织测试。
更好的组织
Swift测试框架允许你使用元数据(如特征和标签)组织测试,或者使用结构体包装测试。因此让我们看看父结构是如何影响测试的组织的。
在这种情况下,我们正在测试针对个人的代码。因此,调用我们的包装器 PersonTests是有意义的:
import Testing
@testable import SwiftTestingPlayground
struct PersonTests {
@Test func fullName() {
let person = Person(firstName: "Jack", lastName: "Chen")
#expect(person.fullName == "Jack Chen")
}
}
我们可以重命名测试方法,使其不再包含“person”,因为外部结构已经足够清楚地说明了这一点。
假设你很快就要编写更多的测试,你可能想更进一步,添加另一个名为 Names 的结构体,只关注与名称相关的测试:
import Testing
@testable import SwiftTestingPlayground
struct PersonTests {
@Test func initialization() {
let person = Person(firstName: "Jack", lastName: "Chen")
#expect(person.firstName == "Jack")
#expect(person.lastName == "Chen")
}
struct Names {
@Test func fullName() {
let person = Person(firstName: "Jack", lastName: "Chen")
#expect(person.fullName == "Jack Chen")
}
}
}