# WWDC 2024 - 了解 Swift Testing - URL: https://www.rayzhao.work/posts/wwdc2024-swift-testing/ - Published: 2024-06-13 - Tags: Swift, iOS, WWDC - Source: src/pages/posts/wwdc2024-swift-testing.mdx > 介绍 WWDC 2024 推出的 Swift Testing,包括核心语法、断言方式,以及它与 XCTest 的关键差异。 ## Building Block ```swift import Testing @testable import DestinationVideo @Test("Check video metadata") func videoMetaData() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } ``` `@Test` 宏用来标记这个函数是用来测试的; 默认访问修饰符是 internal,范围为整个 module,`@testable` 相当于可以解除这个禁制,让测试访问到。 `#expect` 宏用来测试条件是否为真 `#require` 宏用来断言,如果条件为假,会直接抛出异常,立即停止测试 ```swift try #require(session.isValid) session.invalidate() // not executed ``` 也可以用来断言可选类型,当空时退出测试 ```swift let method = try #require(paymentMethods.first) #expect(method.isDefault) // not executed ``` ## Traits - 展示测试描述 - 定制测试用例什么时候跑,是否跑 - 修改测试用例执行的行为 一些内置的 Traits,更多详见[文档](https://developer.apple.com/documentation/testing/traits) ## Test Suite 可以将关联的测试函数放在一起,使用 @Suite 宏,可以有实例变量,也可以使用 init 和 deinit 来添加初始化与销毁的一些逻辑 ```swift struct VideoTests { @Test("Check video metadata") func videoMetadata() { let video = Video(fileName: "By the Lake.mov") let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } @Test func rating() async throws { let video = Video(fileName: "By the Lake.mov") #expect(video.contentRating == "G") } } ``` `@Suite` 实例为每一个 `@Test` 方法都初始化一个新的防止状态共享。所以上面的代码可以改写成: ```swift struct VideoTests { let video = Video(fileName: "By the Lake.mov") @Test("Check video metadata") func videoMetadata() { let expectedMetadata = Metadata(duration: .seconds(90)) #expect(video.metadata == expectedMetadata) } @Test func rating() async throws { #expect(video.contentRating == "G") } } ``` ## Common workflows 运行时条件 Runtime conditions,用来控制测试什么时候跑。 `.enabled(if: ...)` ,当条件为 false 时会跳过测试 ```swift @Test(.enabled(if: AppFeatures.isCommentingEnabled)) // Test 'videoCommenting()' skipped func videoCommenting() { // ... } ``` `.disabled(...)` ,可以描述为什么这个测试被禁用 ```swift @Test(.disabled("Due to a known crash")) func example() { // ... } ``` `.bug(...)`,可以用来关联一个 issue ```swift @Test(.disabled("Due to a known crash"), .bug("example.org/bugs/1234", "Program crashes at ")) func example() { // ... } ``` 可以基于操作系统版本可用性来作为条件,使用 `.available(...)` 而不是 `#available(...)` ```swift // Not recommend @Test func hasRuntimeVersionCheck() { guard #available(macOS 15, *) else { return } } @Test @available(macOS 15, *) func usesNewAPIs() { // ... } ``` 使用 `.tags(...)` 来给测试添加标签,用来分组,方便在 Xcode 中根据标签分组查看 带参数的测试 parameterized testing,使用 arguments ```swift // Before struct VideoContinentsTests { @Test func mentionsFor_A_Beach() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "A Beach")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_By_the_Lake() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "By the Lake")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } @Test func mentionsFor_Camping_in_the_Woods() async throws { let videoLibrary = try await VideoLibrary() let video = try #require(await videoLibrary.video(named: "Camping in the Woods")) #expect(!video.mentionedContinents.isEmpty) #expect(video.mentionedContinents.count <= 3) } // ...and more, similar test functions } // After struct VideoContinentsTests { @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) } } ``` ## Swift Testing and XCTest XCTest 与 Swift Testing 的对比: 更多信息可以查看Migrating a test from XCTest ## Open Source 全平台支持,SPM 命令行、Xcode、VSCode Swift Extension Command line: swift test ## 其他信息 - 原视频:https://developer.apple.com/wwdc24/10179 - Github:https://github.com/apple/swift-testing - 其他相关:利用 Swift Testing 进一步优化测试