swift-testing について
Swift Testing を使う開発者向けのテストガイダンスです。テスト構成、テストダブル、フィクスチャ、統合・スナップショットテスト、XCTest からの移行を扱います。
主な機能
- 01Swift Testing の構文と構成
- 02非同期・パラメータ化テストの手法
- 03モック、スタブ、スパイ、フィクスチャ
- 04統合テストとスナップショットテスト
- 05XCTest からの移行ガイド
活用シーン
- 01Swift Testing のテストスイート作成
- 02非同期コードや複数入力のテスト
- 03テストダブルとテストデータの選択
- 04既存の XCTest テストの移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bocato/ swift-testing-agent-skill --skill swift-testingインストール後、Claude Code で/swift-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/_index.mdasync-testing.mddump-snapshot-testing.mdfixtures.mdintegration-testing.mdmigration-xctest.mdparameterized-tests.mdsnapshot-testing.mdtest-doubles.mdtest-organization.md詳細
Swift Testing
概要
このスキルでは、Swift Testing に関する専門的なガイダンスを提供します。最新の Swift Testing フレームワーク、テストダブル(モック、スタブ、スパイ)、フィクスチャ、統合テスト、スナップショットテスト、XCTest からの移行を扱います。F.I.R.S.T. の原則と Arrange-Act-Assert パターンに従って、信頼性が高く保守しやすいテストを開発者が作成できるよう支援するために使用してください。
エージェントの行動規範(以下のルールに従ってください)
- 新しいテストにはすべて、XCTest ではなく Swift Testing フレームワーク(
@Test、#expect、#require、@Suite)を使用してください。 - テストは常に、明確な Arrange-Act-Assert のフェーズで構成してください。
- F.I.R.S.T. の原則(高速、独立、再現可能、自己検証可能、適時)に従ってください。
- Martin Fowler の分類(Dummy、Fake、Stub、Spy、SpyingStub、Mock)に沿った適切なテストダブルの用語を使用してください。
- フィクスチャはテストターゲットではなく、
#if DEBUGを使用してモデルの近くに配置してください。 - テストダブルはテストターゲットではなく、
#if DEBUGを使用してインターフェースの近くに配置してください。 - 振る舞いの検証より状態の検証を優先してください。テストがよりシンプルになり、壊れにくくなります。
- ソフトアサーション(失敗後も続行)には
#expectを、ハードアサーション(失敗時に停止)には#requireを使用してください。
クイック決定ツリー
開発者がテストに関するガイダンスを必要としている場合は、次の決定ツリーに従ってください。
-
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を参照してください
- 移行ガイドについては
トリアージ優先の手順書(よくあるエラー -> 次に取るべき対応)
- 「XCTAssertEqual が利用できない」/テストを最新化する必要がある
- XCTest から Swift Testing への移行には
references/migration-xctest.mdを使用してください
- XCTest から Swift Testing への移行には
- 非同期コードをテストする必要がある
- 非同期のパターン、確認、タイムアウトについては
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を使用してください
- 統合テストのパターンには
基本構文
基本的なテスト
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. の原則
| 原則 | 説明 | 適用方法 |
|---|---|---|
| 高速 | テストが数ミリ秒で実行される | コストの高い処理をモックする |
| 独立 | テスト同士が依存しない | テストごとに新しいインスタンスを使用する |
| 再現可能 | 毎回同じ結果になる | 日付、ネットワーク、外部依存関係をモックする |
| 自己検証可能 | 成功/失敗を自動で報告する | #expect を使用し、print() に頼らない |
| 適時 | コードと並行してテストを書く | エッジケースにはパラメーター化テストを使用する |
テストダブル早見表
Martin Fowler の定義によると:
| 種類 | 目的 | 検証 |
|---|---|---|
| Dummy | パラメーターを埋めるために使い、実際には使用しません | 該当なし |
| Fake | 簡略化した動作する実装です | 状態 |
| Stub | あらかじめ用意した応答を返します | 状態 |
| Spy | 検証のために呼び出しを記録します | 状態 |
| SpyingStub | Stub と 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- データ構造向けのテキストベースのスナップショットテスト
ベストプラクティスのまとめ
- 新しいテストには Swift Testing を使う - モダンな構文と優れた機能を利用できます
- Arrange-Act-Assert に従う - テスト構造を明確にします
- F.I.R.S.T. の原則を適用する - 高速、独立、反復可能、自己検証可能、適時
- フィクスチャをモデルの近くに配置する -
#if DEBUGガードを付けます - テストダブルをインターフェースの近くに配置する -
#if DEBUGガードを付けます - 状態の検証を優先する - 振る舞いの検証よりシンプルで、壊れにくくなります
- パラメーター化テストを使う - 複数の入力を効率よくテストできます
- テストピラミッドに従う - 80% をユニットテスト、15% を統合テスト、5% を UI テストにします
検証チェックリスト(テストを作成する際)
- テストが Arrange-Act-Assert パターンに従っている
- テスト名が実装ではなく振る舞いを説明している
- フィクスチャがランダムな値ではなく、妥当なデフォルト値を使っている
- テストダブルが最小限である(必要なものだけをスタブ化している)
- 非同期テストが適切なパターン(async/await、確認)を使っている
- テストが高速である(コストの高い操作をモック化している)
- テストが独立している(共有状態がない)
- テストが反復可能である(日付や時刻への依存による不安定さがない)