Swift Testing 框架简介

230 阅读2分钟

背景

苹果在 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")
        }
    }
}