SuperAPI AI 工具集

swift-testing

测试与质量

查看项目

更新于 10-15 14:28

技能介绍

Swift Testing 框架

使用 Swift Testing 框架进行现代测试。不使用 XCTest。

概述

Swift Testing 以基于宏的现代方式取代 XCTest,更简洁、对异步支持更好,并默认并行运行测试。核心原则:如果你学过 XCTest,请忘掉它——Swift Testing 的工作方式不同。

参考资料

Apple 文档

迁移指南

核心概念

断言

宏 | 用途

#expect(expression) | 软检查——失败后继续。用于大多数断言。

#require(expression) | 硬检查——失败即停止测试。仅用于前置条件。

可选值解包

let user = try #require(await fetchUser(id: "123")) #expect(user.id == "123")

测试结构

import Testing @testable import YourModule

@Suite struct FeatureTests { let sut: FeatureType

init() throws {
    sut = FeatureType()
}

@Test("Description of behavior")
func testBehavior() {
    #expect(sut.someProperty == expected)
}

}

断言转换

XCTest | Swift Testing

XCTAssert(expr) | #expect(expr)

XCTAssertEqual(a, b) | #expect(a == b)

XCTAssertNil(a) | #expect(a == nil)

XCTAssertNotNil(a) | #expect(a != nil)

try XCTUnwrap(a) | try #require(a)

XCTAssertThrowsError | #expect(throws: ErrorType.self) { }

XCTAssertNoThrow | #expect(throws: Never.self) { }

错误测试

#expect(throws: (any Error).self) { try riskyOperation() } #expect(throws: NetworkError.self) { try fetch() } #expect(throws: NetworkError.timeout) { try fetch() } #expect(throws: Never.self) { try safeOperation() }

参数化测试

@Test("Validates inputs", arguments: zip( ["a", "b", "c"], [1, 2, 3] )) func testInputs(input: String, expected: Int) { #expect(process(input) == expected) }

警告:多个集合不使用 zip 会生成笛卡尔积。

异步测试

@Test func testAsync() async throws { let result = try await fetchData() #expect(!result.isEmpty) }

确认(Confirmations)

@Test func testCallback() async { await confirmation("callback received") { confirm in let sut = SomeType { confirm() } sut.triggerCallback() } }

标签

extension Tag { @Tag static var fast: Self @Tag static var networking: Self }

@Test(.tags(.fast, .networking)) func testNetworkCall() { }

常见陷阱

过度使用 #require——大多数检查应使用 #expect

忘记状态隔离——每个测试获得一个新实例

意外产生笛卡尔积——配对输入始终使用 zip

未使用 .serialized——对线程不安全的遗留测试应使用

常见错误

过度使用 #require——#require 仅用于前置条件。将其用于普通断言会导致测试在第一次失败时停止,而不是报告所有失败。断言使用 #expect,仅当后续断言依赖该值时才使用 #require。

笛卡尔积错误——@Test(arguments: [a, b], [c, d]) 会生成 4 种组合,而不是 2 种。始终使用 zip 正确配对参数:arguments: zip([a, b], [c, d])。

忘记状态隔离——Swift Testing 为每个测试方法创建新的测试实例。但测试之间的共享状态(静态变量、单例)仍会泄漏。使用依赖注入或在测试之间清理单例。

并行测试冲突——Swift Testing 默认并行运行测试。涉及共享文件、数据库或单例的测试会相互干扰。使用 .serialized 或隔离策略。

未自然使用异步——将异步操作包装在 Task { } 中会违背初衷。直接在测试函数签名中使用 async/await:@Test func testAsync() async throws { }。

确认误用——confirmation 用于验证回调是否被调用。将其用于断言是错误的。断言使用 #expect,回调计数使用 confirmation。