swift-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwift Testing
Swift Testing
Overview
概述
This skill provides expert guidance on Swift Testing, covering the modern Swift Testing framework, test doubles (mocks, stubs, spies), fixtures, integration testing, snapshot testing, and migration from XCTest. Use this skill to help developers write reliable, maintainable tests following F.I.R.S.T. principles and Arrange-Act-Assert patterns.
本技能提供关于Swift Testing的专业指导,涵盖现代Swift Testing框架、测试替身(模拟对象、存根、间谍对象)、测试固件、集成测试、快照测试以及从XCTest的迁移。使用本技能可帮助开发者遵循F.I.R.S.T.原则与Arrange-Act-Assert模式,编写可靠、可维护的测试。
Agent Behavior Contract (Follow These Rules)
Agent行为约定(请遵循以下规则)
- Use Swift Testing framework (,
@Test,#expect,#require) for all new tests, not XCTest.@Suite - Always structure tests with clear Arrange-Act-Assert phases.
- Follow F.I.R.S.T. principles: Fast, Isolated, Repeatable, Self-Validating, Timely.
- Use proper test double terminology per Martin Fowler's taxonomy (Dummy, Fake, Stub, Spy, SpyingStub, Mock).
- Place fixtures close to models with , not in test targets.
#if DEBUG - Place test doubles close to interfaces with , not in test targets.
#if DEBUG - Prefer state verification over behavior verification - simpler, less brittle tests.
- Use for soft assertions (continue on failure) and
#expectfor hard assertions (stop on failure).#require
- 所有新测试均使用Swift Testing框架(、
@Test、#expect、#require),而非XCTest。@Suite - 始终以清晰的Arrange-Act-Assert阶段构建测试。
- 遵循F.I.R.S.T.原则:Fast(快速)、Isolated(独立)、Repeatable(可重复)、Self-Validating(自验证)、Timely(及时)。
- 采用Martin Fowler分类法中的标准测试替身术语(Dummy、Fake、Stub、Spy、SpyingStub、Mock)。
- 测试固件需通过标记放置在模型附近,而非测试目标中。
#if DEBUG - 测试替身需通过标记放置在接口附近,而非测试目标中。
#if DEBUG - 优先使用状态验证而非行为验证——测试更简单、更不易失效。
- 使用进行软断言(失败后继续执行测试),使用
#expect进行硬断言(失败后终止测试)。#require
Quick Decision Tree
快速决策树
When a developer needs testing guidance, follow this decision tree:
-
Starting fresh with Swift Testing?
- Read for suites, tags, traits
references/test-organization.md - Read for async test patterns
references/async-testing.md
- Read
-
Need to create test data?
- Read for fixture patterns and placement
references/fixtures.md - Read for mock/stub/spy patterns
references/test-doubles.md
- Read
-
Testing multiple inputs?
- Read for parameterized testing
references/parameterized-tests.md
- Read
-
Testing module interactions?
- Read for integration test patterns
references/integration-testing.md
- Read
-
Testing UI for regressions?
- Read for snapshot testing setup
references/snapshot-testing.md
- Read
-
Testing data structures or state?
- Read for text-based snapshot testing
references/dump-snapshot-testing.md
- Read
-
Migrating from XCTest?
- Read for migration guide
references/migration-xctest.md
- Read
当开发者需要测试指导时,请遵循以下决策树:
-
刚开始使用Swift Testing?
- 阅读了解测试套件、标签、特性
references/test-organization.md - 阅读了解异步测试模式
references/async-testing.md
- 阅读
-
需要创建测试数据?
- 阅读了解测试固件模式与放置位置
references/fixtures.md - 阅读了解模拟对象/存根/间谍对象模式
references/test-doubles.md
- 阅读
-
需要测试多组输入?
- 阅读了解参数化测试
references/parameterized-tests.md
- 阅读
-
需要测试模块交互?
- 阅读了解集成测试模式
references/integration-testing.md
- 阅读
-
需要测试UI回归问题?
- 阅读了解快照测试设置
references/snapshot-testing.md
- 阅读
-
需要测试数据结构或状态?
- 阅读了解基于文本的快照测试
references/dump-snapshot-testing.md
- 阅读
-
从XCTest迁移?
- 阅读了解迁移指南
references/migration-xctest.md
- 阅读
Triage-First Playbook (Common Errors -> Next Best Move)
优先排查手册(常见错误 -> 最佳解决方案)
- "XCTAssertEqual is unavailable" / need to modernize tests
- Use for XCTest to Swift Testing migration
references/migration-xctest.md
- Use
- Need to test async code
- Use for async patterns, confirmation, timeouts
references/async-testing.md
- Use
- Tests are slow or flaky
- Check F.I.R.S.T. principles, use proper mocking per
references/test-doubles.md
- Check F.I.R.S.T. principles, use proper mocking per
- Need deterministic test data
- Use for fixture patterns with fixed dates
references/fixtures.md
- Use
- Need to test multiple scenarios efficiently
- Use for parameterized testing
references/parameterized-tests.md
- Use
- Need to verify component interactions
- Use for integration test patterns
references/integration-testing.md
- Use
- "XCTAssertEqual不可用" / 需要现代化测试
- 使用进行XCTest到Swift Testing的迁移
references/migration-xctest.md
- 使用
- 需要测试异步代码
- 使用了解异步模式、确认机制、超时处理
references/async-testing.md
- 使用
- 测试运行缓慢或不稳定
- 检查是否遵循F.I.R.S.T.原则,按照使用正确的模拟方式
references/test-doubles.md
- 检查是否遵循F.I.R.S.T.原则,按照
- 需要确定性测试数据
- 使用中的固定日期测试固件模式
references/fixtures.md
- 使用
- 需要高效测试多种场景
- 使用进行参数化测试
references/parameterized-tests.md
- 使用
- 需要验证组件交互
- 使用中的集成测试模式
references/integration-testing.md
- 使用
Core Syntax
核心语法
Basic Test
基础测试
swift
import Testing
@Test func basicTest() {
#expect(1 + 1 == 2)
}swift
import Testing
@Test func basicTest() {
#expect(1 + 1 == 2)
}Test with Description
带描述的测试
swift
@Test("Adding items increases cart count")
func addItem() {
let cart = Cart()
cart.add(item)
#expect(cart.count == 1)
}swift
@Test("添加商品会增加购物车数量")
func addItem() {
let cart = Cart()
cart.add(item)
#expect(cart.count == 1)
}Async Test
异步测试
swift
@Test func asyncOperation() async throws {
let result = try await service.fetch()
#expect(result.isValid)
}swift
@Test func asyncOperation() async throws {
let result = try await service.fetch()
#expect(result.isValid)
}Arrange-Act-Assert Pattern
Arrange-Act-Assert模式
Structure every test with clear phases:
swift
@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)
}所有测试均需按清晰的阶段构建:
swift
@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)
}Assertions
断言
#expect - Soft Assertion
#expect - 软断言
Continues test execution after failure:
swift
@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
}失败后继续执行测试:
swift
@Test func multipleExpectations() {
let user = User(name: "Alice", age: 30)
#expect(user.name == "Alice") // 若失败,测试继续执行
#expect(user.age == 30) // 此断言仍会运行
}#require - Hard Assertion
#require - 硬断言
Stops test execution on failure:
swift
@Test func requireExample() throws {
let user = try #require(fetchUser()) // Stops if nil
#expect(user.name == "Alice")
}失败后终止测试执行:
swift
@Test func requireExample() throws {
let user = try #require(fetchUser()) // 若为nil则终止测试
#expect(user.name == "Alice")
}Error Testing
错误测试
swift
@Test func throwsError() {
#expect(throws: ValidationError.self) {
try validate(invalidInput)
}
}
@Test func throwsSpecificError() {
#expect(throws: ValidationError.emptyField) {
try validate("")
}
}swift
@Test func throwsError() {
#expect(throws: ValidationError.self) {
try validate(invalidInput)
}
}
@Test func throwsSpecificError() {
#expect(throws: ValidationError.emptyField) {
try validate("")
}
}F.I.R.S.T. Principles
F.I.R.S.T.原则
| Principle | Description | Application |
|---|---|---|
| Fast | Tests execute in milliseconds | Mock expensive operations |
| Isolated | Tests don't depend on each other | Fresh instance per test |
| Repeatable | Same result every time | Mock dates, network, external deps |
| Self-Validating | Auto-report pass/fail | Use |
| Timely | Write tests alongside code | Use parameterized tests for edge cases |
| Principle | 描述 | 应用 |
|---|---|---|
| Fast | 测试以毫秒级运行 | 模拟昂贵(耗时/资源密集)的操作 |
| Isolated | 测试之间互不依赖 | 每个测试使用全新实例 |
| Repeatable | 每次运行结果一致 | 模拟日期、网络、外部依赖 |
| Self-Validating | 自动报告通过/失败 | 使用 |
| Timely | 与代码同步编写 | 使用参数化测试覆盖边缘案例 |
Test Double Quick Reference
测试速查
| Type | Purpose | Verification |
|---|---|---|
| Dummy | Fill parameters, never used | N/A |
| Fake | Working implementation with shortcuts | State |
| Stub | Provides canned answers | State |
| Spy | Records calls for verification | State |
| SpyingStub | Stub + Spy combined (most common) | State |
| Mock | Pre-programmed expectations, self-verifies | Behavior |
Important: What Swift community calls "Mock" is usually a SpyingStub.
For detailed patterns, see .
references/test-doubles.md| 类型 | 用途 | 验证方式 |
|---|---|---|
| Dummy | 填充参数,从未被使用 | 无 |
| Fake | 带简化实现的可用版本 | 状态 |
| Stub | 提供预设响应 | 状态 |
| Spy | 记录调用情况用于验证 | 状态 |
| SpyingStub | 存根+间谍的组合(最常用) | 状态 |
| Mock | 预定义期望并自行验证 | 行为 |
重要提示:Swift社区中所谓的"Mock"通常指SpyingStub。
如需详细模式,请查看。
references/test-doubles.mdTest Double Placement
测试替身放置
Place test doubles close to the interface, not in test targets:
swift
// 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测试替身需放置在接口附近,而非测试目标中:
swift
// 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: 捕获的调用记录
public private(set) var savedRecords: [PersonalRecord] = []
// Stub: 可配置的响应
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)
}
}
#endifFixtures
测试固件
Place fixtures close to the model:
swift
// 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)
}
}
#endifFor detailed patterns, see .
references/fixtures.md测试固件需放置在模型附近:
swift
// 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
// ... 所有属性的默认值
) -> PersonalRecord {
PersonalRecord(id: id, weight: weight)
}
}
#endif如需详细模式,请查看。
references/fixtures.mdTest Pyramid
测试金字塔
+-------------+
| UI Tests | 5% - End-to-end flows
| (E2E) |
+-------------+
| Integration | 15% - Module interactions
| Tests |
+-------------+
| Unit | 80% - Individual components
| Tests |
+-------------+ +-------------+
| UI测试 | 5% - 端到端流程
| (E2E) |
+-------------+
| 集成测试 | 15% - 模块交互
| |
+-------------+
| 单元测试 | 80% - 独立组件
| |
+-------------+Reference Files
参考文件
Load these files as needed for specific topics:
- - Suites, tags, traits, parallel execution
test-organization.md - - Testing multiple inputs efficiently
parameterized-tests.md - - Async patterns, confirmation, timeouts, cancellation
async-testing.md - - Complete XCTest to Swift Testing migration guide
migration-xctest.md - - Complete taxonomy with examples (Dummy, Fake, Stub, Spy, SpyingStub, Mock)
test-doubles.md - - Fixture patterns, placement, and best practices
fixtures.md - - Module interaction testing patterns
integration-testing.md - - UI regression testing with SnapshotTesting library
snapshot-testing.md - - Text-based snapshot testing for data structures
dump-snapshot-testing.md
根据特定主题需求加载以下文件:
- - 测试套件、标签、特性、并行执行
test-organization.md - - 高效测试多组输入
parameterized-tests.md - - 异步模式、确认机制、超时处理、取消操作
async-testing.md - - XCTest到Swift Testing的完整迁移指南
migration-xctest.md - - 含示例的完整分类(Dummy、Fake、Stub、Spy、SpyingStub、Mock)
test-doubles.md - - 测试固件模式、放置位置与最佳实践
fixtures.md - - 模块交互测试模式
integration-testing.md - - 使用SnapshotTesting库进行UI回归测试
snapshot-testing.md - - 针对数据结构的基于文本的快照测试
dump-snapshot-testing.md
Best Practices Summary
最佳实践总结
- Use Swift Testing for new tests - Modern syntax, better features
- Follow Arrange-Act-Assert - Clear test structure
- Apply F.I.R.S.T. principles - Fast, Isolated, Repeatable, Self-Validating, Timely
- Place fixtures near models - With guards
#if DEBUG - Place test doubles near interfaces - With guards
#if DEBUG - Prefer state verification - Simpler, less brittle than behavior verification
- Use parameterized tests - For testing multiple inputs efficiently
- Follow test pyramid - 80% unit, 15% integration, 5% UI
- 新测试使用Swift Testing - 现代语法,更优特性
- 遵循Arrange-Act-Assert - 清晰的测试结构
- 应用F.I.R.S.T.原则 - 快速、独立、可重复、自验证、及时
- 测试固件靠近模型放置 - 使用保护
#if DEBUG - 测试替身靠近接口放置 - 使用保护
#if DEBUG - 优先使用状态验证 - 比行为验证更简单、更不易失效
- 使用参数化测试 - 高效测试多组输入
- 遵循测试金字塔 - 80%单元测试、15%集成测试、5%UI测试
Verification Checklist (When You Write Tests)
验证清单(编写测试时)
- Tests follow Arrange-Act-Assert pattern
- Test names describe behavior, not implementation
- Fixtures use sensible defaults, not random values
- Test doubles are minimal (only stub what's needed)
- Async tests use proper patterns (async/await, confirmation)
- Tests are fast (mock expensive operations)
- Tests are isolated (no shared state)
- Tests are repeatable (no flaky date/time dependencies)
- 测试遵循Arrange-Act-Assert模式
- 测试名称描述行为而非实现
- 测试固件使用合理默认值,而非随机值
- 测试替身保持最小化(仅存根必要内容)
- 异步测试使用正确模式(async/await、确认机制)
- 测试运行快速(模拟耗时操作)
- 测试相互独立(无共享状态)
- 测试结果可重复(无依赖日期/时间的不稳定因素)