mcpField
S
swift-testing

swift-testing

github.com/bocato测试与质量80283 次安装39 周前更新
代码仓库

关于 swift-testing

面向 Swift 开发者的 Swift Testing 测试指导技能,涵盖测试组织、测试替身、固定数据、集成与快照测试,以及从 XCTest 迁移。

主要功能

  1. 01Swift Testing 语法与组织
  2. 02异步和参数化测试模式
  3. 03Mock、Stub、Spy 与固定数据
  4. 04集成测试与快照测试
  5. 05XCTest 迁移指导

适用场景

  1. 01新建 Swift Testing 测试套件
  2. 02测试异步代码和多组输入
  3. 03选择测试替身与测试数据
  4. 04迁移现有 XCTest 测试

安装

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

$npx skills add https://github.com/bocato/swift-testing-agent-skill --skill swift-testing

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
_index.md
async-testing.md
dump-snapshot-testing.md
fixtures.md
integration-testing.md
migration-xctest.md
parameterized-tests.md
snapshot-testing.md
test-doubles.md
test-organization.md

详细介绍

Swift Testing

概述

此技能提供 Swift Testing 专业指南,涵盖现代 Swift Testing 框架、测试替身(模拟对象、桩对象、间谍对象)、测试固件、集成测试、快照测试,以及从 XCTest 迁移。使用此技能帮助开发者遵循 F.I.R.S.T. 原则和 Arrange-Act-Assert 模式,编写可靠且易于维护的测试。

Agent 行为约定(遵循以下规则)

  1. 所有新测试都使用 Swift Testing 框架(@Test、#expect、#require、@Suite),不要使用 XCTest。
  2. 始终将测试划分为清晰的 Arrange-Act-Assert 阶段。
  3. 遵循 F.I.R.S.T. 原则:快速(Fast)、隔离(Isolated)、可重复(Repeatable)、自验证(Self-Validating)、及时(Timely)。
  4. 根据 Martin Fowler 的分类体系使用恰当的测试替身术语(Dummy、Fake、Stub、Spy、SpyingStub、Mock)。
  5. 将测试固件放在靠近模型的位置,并使用 #if DEBUG,不要放在测试目标中。
  6. 将测试替身放在靠近接口的位置,并使用 #if DEBUG,不要放在测试目标中。
  7. 优先采用状态验证,而非行为验证——测试更简单,也更不易脆弱。
  8. 软断言(失败后继续执行)使用 #expect,硬断言(失败后停止执行)使用 #require。

快速决策树

开发者需要测试指导时,请遵循此决策树:

  1. 刚开始使用 Swift Testing?

    • 阅读 references/test-organization.md,了解测试套件、标签和特征
    • 阅读 references/async-testing.md,了解异步测试模式
  2. 需要创建测试数据?

    • 阅读 references/fixtures.md,了解测试固件模式及其放置位置
    • 阅读 references/test-doubles.md,了解 Mock/Stub/Spy 模式
  3. 需要测试多个输入?

    • 阅读 references/parameterized-tests.md,了解参数化测试
  4. 需要测试模块间的交互?

    • 阅读 references/integration-testing.md,了解集成测试模式
  5. 需要测试 UI 是否出现回归?

    • 阅读 references/snapshot-testing.md,了解快照测试设置
  6. 需要测试数据结构或状态?

    • 阅读 references/dump-snapshot-testing.md,了解基于文本的快照测试
  7. 需要从 XCTest 迁移?

    • 阅读 references/migration-xctest.md,了解迁移指南

优先分诊手册(常见错误 -> 下一步最佳操作)

  • “XCTAssertEqual 不可用” / 需要让测试现代化
    • 使用 references/migration-xctest.md 将 XCTest 迁移到 Swift Testing
  • 需要测试异步代码
    • 使用 references/async-testing.md 了解异步模式、确认机制和超时
  • 测试运行缓慢或不稳定
    • 检查 F.I.R.S.T. 原则,并根据 references/test-doubles.md 使用恰当的模拟
  • 需要确定性测试数据
    • 使用 references/fixtures.md 了解使用固定日期的测试固件模式
  • 需要高效测试多个场景
    • 使用 references/parameterized-tests.md 进行参数化测试
  • 需要验证组件间的交互
    • 使用 references/integration-testing.md 了解集成测试模式

核心语法

基本测试
import Testing

@Test func basicTest() {
    #expect(1 + 1 == 2)
}
带描述的测试
@Test("Adding items increases cart count")
func addItem() {
    let cart = Cart()
    cart.add(item)
    #expect(cart.count == 1)
}
异步测试
@Test func asyncOperation() async throws {
    let result = try await service.fetch()
    #expect(result.isValid)
}

Arrange-Act-Assert 模式

将每个测试划分为清晰的阶段:

@Test func calculateTotal() {
    // Given
    let cart = ShoppingCart()
    cart.add(Item(price: 10))
    cart.add(Item(price: 20))

    // When
    let total = cart.calculateTotal()

    // Then
    #expect(total == 30)
}

断言

#expect - 软断言

失败后继续执行测试:

@Test func multipleExpectations() {
    let user = User(name: "Alice", age: 30)
    #expect(user.name == "Alice")  // If fails, test continues
    #expect(user.age == 30)        // This still runs
}
#require - 硬断言

失败后停止执行测试:

@Test func requireExample() throws {
    let user = try #require(fetchUser())  // Stops if nil
    #expect(user.name == "Alice")
}
错误测试
@Test func throwsError() {
    #expect(throws: ValidationError.self) {
        try validate(invalidInput)
    }
}

@Test func throwsSpecificError() {
    #expect(throws: ValidationError.emptyField) {
        try validate("")
    }
}

F.I.R.S.T. 原则

原则描述应用
快速(Fast)测试在毫秒内执行模拟开销大的操作
隔离(Isolated)测试之间互不依赖每项测试使用全新实例
可重复(Repeatable)每次结果都相同模拟日期、网络和外部依赖
自验证(Self-Validating)自动报告通过/失败使用 #expect,绝不依赖 print()
及时(Timely)编写代码的同时编写测试使用参数化测试覆盖边界情况

测试替身速查

根据 Martin Fowler 的定义:

类型用途验证方式
Dummy填充参数,从不使用不适用
Fake带有简化实现的可用实现状态
Stub提供预设答案状态
Spy记录调用以供验证状态
SpyingStubStub + Spy 的组合(最常见)状态
Mock预先编程的期望值,可自行验证行为

重要提示:Swift 社区所说的 “Mock” 通常是 SpyingStub。

详细模式请参阅 references/test-doubles.md。

测试替身的放置位置

将测试替身放在靠近接口的位置,而不是测试目标中:

// In PersonalRecordsCore-Interface/Sources/...

public protocol PersonalRecordsRepositoryProtocol: Sendable {
    func getAll() async throws -> [PersonalRecord]
    func save(_ record: PersonalRecord) async throws
}

#if DEBUG
public final class PersonalRecordsRepositorySpyingStub: PersonalRecordsRepositoryProtocol {
    // Spy: Captured calls
    public private(set) var savedRecords: [PersonalRecord] = []

    // Stub: Configurable responses
    public var recordsToReturn: [PersonalRecord] = []
    public var errorToThrow: Error?

    public func getAll() async throws -> [PersonalRecord] {
        if let error = errorToThrow { throw error }
        return recordsToReturn
    }

    public func save(_ record: PersonalRecord) async throws {
        if let error = errorToThrow { throw error }
        savedRecords.append(record)
    }
}
#endif

夹具

将夹具放在靠近模型的位置:

// In Sources/Models/PersonalRecord.swift

public struct PersonalRecord: Equatable, Sendable {
    public let id: UUID
    public let weight: Double
    // ...
}

#if DEBUG
extension PersonalRecord {
    public static func fixture(
        id: UUID = UUID(),
        weight: Double = 100.0
        // ... defaults for all properties
    ) -> PersonalRecord {
        PersonalRecord(id: id, weight: weight)
    }
}
#endif

详细模式请参阅 references/fixtures.md。

测试金字塔

        +-------------+
        |   UI Tests  |  5%  - End-to-end flows
        |   (E2E)     |
        +-------------+
        | Integration |  15% - Module interactions
        |    Tests    |
        +-------------+
        |    Unit     |  80% - Individual components
        |    Tests    |
        +-------------+

参考文件

根据需要加载以下文件,了解特定主题:

  • test-organization.md - 测试套件、标签、特征和并行执行
  • parameterized-tests.md - 高效测试多个输入
  • async-testing.md - 异步模式、确认、超时和取消
  • migration-xctest.md - 从 XCTest 迁移到 Swift Testing 的完整指南
  • test-doubles.md - 含示例的完整分类(Dummy、Fake、Stub、Spy、SpyingStub、Mock)
  • fixtures.md - 夹具模式、放置位置和最佳实践
  • integration-testing.md - 模块交互测试模式
  • snapshot-testing.md - 使用 SnapshotTesting 库进行 UI 回归测试
  • dump-snapshot-testing.md - 对数据结构进行基于文本的快照测试

最佳实践总结

  1. 新测试使用 Swift Testing - 语法现代,功能更完善
  2. 遵循 Arrange-Act-Assert - 保持清晰的测试结构
  3. 应用 F.I.R.S.T. 原则 - 快速、隔离、可重复、自我验证、及时
  4. 将夹具放在模型附近 - 使用 #if DEBUG 保护
  5. 将测试替身放在接口附近 - 使用 #if DEBUG 保护
  6. 优先验证状态 - 比验证行为更简单,也更不易脆弱
  7. 使用参数化测试 - 高效测试多个输入
  8. 遵循测试金字塔 - 80% 单元测试、15% 集成测试、5% UI 测试

验证清单(编写测试时)

  • 测试遵循 Arrange-Act-Assert 模式
  • 测试名称描述行为,而非实现
  • 夹具使用合理的默认值,而非随机值
  • 测试替身保持精简(只 Stub 必要内容)
  • 异步测试使用恰当的模式(async/await、confirmation)
  • 测试运行迅速(模拟开销大的操作)
  • 测试相互隔离(无共享状态)
  • 测试可重复运行(不依赖不稳定的日期/时间)

评论

支持 Markdown