mcpField
S
swift-testing

swift-testing

github.com/bocatoテスト・品質80283 回インストール39 週間前に更新
リポジトリ

swift-testing について

Swift Testing を使う開発者向けのテストガイダンスです。テスト構成、テストダブル、フィクスチャ、統合・スナップショットテスト、XCTest からの移行を扱います。

主な機能

  1. 01Swift Testing の構文と構成
  2. 02非同期・パラメータ化テストの手法
  3. 03モック、スタブ、スパイ、フィクスチャ
  4. 04統合テストとスナップショットテスト
  5. 05XCTest からの移行ガイド

活用シーン

  1. 01Swift 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 パターンに従って、信頼性が高く保守しやすいテストを開発者が作成できるよう支援するために使用してください。

エージェントの行動規範(以下のルールに従ってください)

  1. 新しいテストにはすべて、XCTest ではなく Swift Testing フレームワーク(@Test、#expect、#require、@Suite)を使用してください。
  2. テストは常に、明確な Arrange-Act-Assert のフェーズで構成してください。
  3. F.I.R.S.T. の原則(高速、独立、再現可能、自己検証可能、適時)に従ってください。
  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 を参照してください
  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 が利用できない」/テストを最新化する必要がある
    • XCTest から Swift Testing への移行には references/migration-xctest.md を使用してください
  • 非同期コードをテストする必要がある
    • 非同期のパターン、確認、タイムアウトについては 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. の原則

原則説明適用方法
高速テストが数ミリ秒で実行されるコストの高い処理をモックする
独立テスト同士が依存しないテストごとに新しいインスタンスを使用する
再現可能毎回同じ結果になる日付、ネットワーク、外部依存関係をモックする
自己検証可能成功/失敗を自動で報告する#expect を使用し、print() に頼らない
適時コードと並行してテストを書くエッジケースにはパラメーター化テストを使用する

テストダブル早見表

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 パターンに従っている
  • テスト名が実装ではなく振る舞いを説明している
  • フィクスチャがランダムな値ではなく、妥当なデフォルト値を使っている
  • テストダブルが最小限である(必要なものだけをスタブ化している)
  • 非同期テストが適切なパターン(async/await、確認)を使っている
  • テストが高速である(コストの高い操作をモック化している)
  • テストが独立している(共有状態がない)
  • テストが反復可能である(日付や時刻への依存による不安定さがない)

コメント