mcpField
S
swift-testing

swift-testing

github.com/johnrogers测试与质量233206 次安装41 周前更新
代码仓库

关于 swift-testing

面向 Swift 开发者的 Swift Testing 框架指南,适用于替换 XCTest。内容涵盖宏断言、异步与参数化测试,以及迁移时的常见问题。

主要功能

  1. 01基于宏的 #expect 与 #require 断言
  2. 02异步测试与回调确认
  3. 03通过参数配对构建参数化测试
  4. 04测试标签与并行执行注意事项
  5. 05XCTest 断言迁移方式

适用场景

  1. 01将 Swift 测试套件中的 XCTest 断言替换为新写法
  2. 02测试异步函数和回调行为
  3. 03为参数化测试创建配对输入

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/johnrogers/claude-swift-engineering --skill swift-testing

安装后在 Claude Code 中输入/swift-testing确认已加载。

详细介绍

Swift Testing 测试框架

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

概述

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

参考资料

核心概念

断言
宏用途
#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)
    }
}

断言转换

XCTestSwift 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)
}
确认
@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() { }

常见陷阱

  1. 过度使用 #require——大多数检查应使用 #expect
  2. 忘记状态隔离——每个测试都会获得一个全新的实例
  3. 意外产生笛卡尔积——成对输入始终使用 zip
  4. 未使用 .serialized——用于线程不安全的旧版测试

常见错误

  1. 过度使用 #require——#require 仅用于前置条件。将它用于常规断言会导致测试在首次失败时停止,而不是报告所有失败。断言应使用 #expect;只有当后续断言依赖该值时,才使用 #require。

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

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

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

  5. 未自然地使用 async——将异步操作包装在 Task { } 中会失去其意义。直接在测试函数签名中使用 async/await:@Test func testAsync() async throws { }。

  6. 误用确认机制——confirmation 用于验证是否调用了回调。将其用于断言是不正确的。断言使用 #expect,回调次数使用 confirmation。

评论

支持 Markdown