WWDC24(Xcode 16)中全新的 Swift Testing 使用进阶

概述

WWDC 24 祭出的全新单元测试系统着实让苹果开发者们眼前一亮。"原来测试还可以这么爽!?",日渐逼近蟋蟀发型的某位码农如是说。

Swift Testing 在简洁性以及灵活性全面超越老大哥 XCTest 的同时,也让秃头码农们真正见识到了 Swifty 范儿测试的灵魂。

在本篇博文中,您将学到如下内容:

  • 概述
  • [1. 如何为测试加上"构造"和"析构"器](#1. 如何为测试加上“构造”和“析构”器)
  • [2. 如何依次串行测试?](#2. 如何依次串行测试?)
  • 总结

本篇是《用接地气的例子趣谈 WWDC 24 全新的 Swift Testing 入门》系列博文的进阶学习,希望大家喜欢。

那还等什么呢?Let's Testing!!!😉


1. 如何为测试加上"构造"和"析构"器

在以往的 XCTest 测试系统中,我们是用 XCTestCase 派生子类中的特定方法来完成每个测试的初始化和清理操作的:

swift 复制代码
final class MyUnitTests: XCTestCase {

    // 初始化测试
    override func setUpWithError() throws {
        continueAfterFailure = false

    }

    // 清理测试
    override func tearDownWithError() throws {
    }
}

而在新的 Swift Testing 中,我们只需使用类型本就具有的构造和析构器即可。

比如在下面的测试中,我们将所有独立的测试方法一并放到 ModelTests 结构中,然后借助结构的构造器来完成测试的初始化工作:

swift 复制代码
struct ModelTests {
    let container: ModelContainer
    
    // 测试前的所有初始化操作统统进来!!!
    init() throws {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        container = try ModelContainer(for: User.self, configurations: config)
    }
    
    @Test func verifyBulkImport() throws {
        User.bulkImport()
        
        let modelContext = ModelContext(container)
        let count = try modelContext.fetchCount(FetchDescriptor<User>())
        
        #expect(count == 100)
    }
}

类似的,如果我们需要在测试之后完成一些必要的清理工作,同样可以将上面的结构"升级"为类,然后利用类的析构方法来搞定它们:

swift 复制代码
class ModelTests {
    let container: ModelContainer
    
    init() throws {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        container = try ModelContainer(for: User.self, configurations: config)
    }
    
    // 利用类的析构器来将测试之后的一切清理干净!
    deinit {
        container.deleteAllData()
    }
    
    @Test func verifyBulkImport() throws {
        User.bulkImport()
        
        let modelContext = ModelContext(container)
        let count = try modelContext.fetchCount(FetchDescriptor<User>())
        
        #expect(count == 100)
    }
}

如您所见:在 Swift Testing 中我们没有搞任何"特殊行为",仅仅借助结构和类的"原始本能"即可轻松将测试的初始化和清扫工作化为无形,就问大家赞不赞呢?

2. 如何依次串行测试?

在包含海量 Testing 方法的单元测试中,为了达到客观的"公平公正",我们往往需要这些方法随机执行:即每次测试它们的执行顺序都是不确定的。这样做到好处是可以将可能出现的代码依赖关系降到最低。

这还不算完!为了最大化保证测试的"实事求是",我们还需要这些测试的执行可以重叠,即它们可以并发运行

swift 复制代码
struct ModelTests {
    
    var model = Model.shared
    
    @Test("测试创建 Itmes", .tags(.createItem))
    mutating func createItems() {
        print("#1 start")
        model.createItems()
        #expect(!model.items.isEmpty, "应该成功创建若干 Items!")
        print("#1 end")
    }
    
    @Test("测试删除所有 Item")
    mutating func delItem(){
        print("#2 start")
        model.createItems()
        #expect(!model.items.isEmpty)
        model.deleteAllItems()
        #expect(model.items.isEmpty, "应该成功删除所有 Items!")
        print("#2 end")
    }
    
    @Test("无厘头测试")
    mutating func noHead(){
        print("#3 start")
        #expect(true)
        print("#3 end")
    }
}

在上面的 ModelTests 测试中,多次运行可以发现其中 3 个测试方法的执行顺序皆是变幻无穷。甚至某个方法还未完成,另一个方法就"跃跃欲试"了:

swift 复制代码
◇ Test run started.
↳ Testing Library Version: 102 (arm64-apple-ios13.0-simulator)
◇ Suite ModelTests started.
◇ Test "测试创建 Itmes" started.
◇ Test "测试删除所有 Item" started.
◇ Test "无厘头测试" started.
#2 start
#3 start
#2 end
#3 end
✔ Test "测试删除所有 Item" passed after 0.001 seconds.
✔ Test "无厘头测试" passed after 0.001 seconds.
#1 start
#1 end
✔ Test "测试创建 Itmes" passed after 0.001 seconds.
✔ Suite ModelTests passed after 0.001 seconds.
✔ Test run with 3 tests passed after 0.001 seconds.

但在某些场景中,我们可能也需要测试方法能够按照它们定义的顺序依次串行执行。这在 Swift Testing 里非常容易,简直轻而易举!

只需使用 @Suite 宏即可优雅的"樽前月下":

更具体的说,在 @Suite 宏的若干 SuiteTrait 中包含一个 serialized 特性,它就是测试里我们那个"真命天子":

还拿上面的 ModelTests 测试结构来说,其中的内容无需做任何改变,只需用 @Suite 宏如此这般修饰它即可:

swift 复制代码
@Suite(.serialized)
struct ModelTests {...}

再次运行测试可以看到,所有测试方法都会"言听计从"的自上而下串行执行了:

swift 复制代码
◇ Test run started.
↳ Testing Library Version: 102 (arm64-apple-ios13.0-simulator)
◇ Suite ModelTests started.
◇ Test "测试创建 Itmes" started.
#1 start
#1 end
✔ Test "测试创建 Itmes" passed after 0.001 seconds.
◇ Test "测试删除所有 Item" started.
#2 start
#2 end
✔ Test "测试删除所有 Item" passed after 0.001 seconds.
◇ Test "无厘头测试" started.
#3 start
#3 end
✔ Test "无厘头测试" passed after 0.001 seconds.
✔ Suite ModelTests passed after 0.001 seconds.
✔ Test run with 3 tests passed after 0.001 seconds.

当然如果我们希望的话,还可以利用 @Suite 宏丰富多彩的构造器来让测试更加"银杏化":

swift 复制代码
@Suite("大熊猫侯佩的大测试", .serialized)
struct ModelTests {...}

现在,相信小伙伴们对 Xcode 16 中崭新的 Swift Testing 都轻车熟路、了如指掌了吧?棒棒哒!💯


更多关于 Swift Testing 测试系统的介绍,请小伙伴们移步如下链接观赏精彩的内容:

想要系统学习 Swift 的小伙伴们,欢迎到我的《Swift语言开发精讲》专栏来逛一逛哦:


总结

在本篇博文中,我们进一步讨论了 WWDC24(Xcode 16)中全新 Swift Testing 的驾驭之道,并用多个轻巧的"栗子"让小伙伴们明白晓畅。

感谢观赏,再会啦!😎

相关推荐
程序猿000001号2 小时前
探索Python的pytest库:简化单元测试的艺术
python·单元测试·pytest
星蓝_starblue1 天前
单元测试(C++)——gmock通用测试模版(个人总结)
c++·单元测试·log4j
whynogome1 天前
单元测试使用记录
单元测试
字节程序员1 天前
使用JUnit进行集成测试
jmeter·junit·单元测试·集成测试·压力测试
love静思冥想2 天前
Java 单元测试中 JSON 相关的测试案例
java·单元测试·json
乐闻x4 天前
如何使用 TypeScript 和 Jest 编写高质量单元测试
javascript·typescript·单元测试·jest
Cachel wood4 天前
Vue.js前端框架教程4:Vue响应式变量和指令(Directives)
前端·vue.js·windows·python·单元测试·django·前端框架
@TangXin4 天前
单元测试-Unittest框架实践
单元测试
十年一梦实验室4 天前
【C++】sophus : test_macros.hpp 用于单元测试的宏和辅助函数 (四)
开发语言·c++·单元测试
编码浪子5 天前
Springboot3.x配置类(Configuration)和单元测试
单元测试