用接地气的例子趣谈 WWDC 24 全新的 Swift Testing 入门(一)

271 阅读4分钟

在这里插入图片描述

概述

从 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 有以下几点不同:

  1. 不需要先创建派生自 XCTestCase 的测试类;
  2. 直接写测试逻辑,简单明了、一发入魂;
  3. 测试方法名无需限定于特定前缀,而是用 @Test 宏来修饰;

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 中一些重要宏的应用场景,敬请期待吧!

总结

在本篇博文中,我们介绍了 WWDC 24(Xcode 16)新引入的 Swift Testing 测试系统、比较了它和 XCTest 的区别、如何集成到 Xcode 项目中,并用一个鲜活的例子讨论了它的金辉玉洁。

感谢观赏,下篇再会啦!8-)