mcpField
S
swift-testing

swift-testing

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

swift-testing について

XCTest からの移行を考える Swift 開発者向けの Swift Testing ガイドです。マクロによるアサーション、非同期・パラメータ化テスト、移行時の注意点を扱います。

主な機能

  1. 01#expect と #require によるマクロアサーション
  2. 02非同期テストとコールバック確認
  3. 03引数を組み合わせるパラメータ化テスト
  4. 04テストタグと並列実行の注意点
  5. 05XCTest アサーションの移行方法

活用シーン

  1. 01Swift のテストスイートで 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]) は2つではなく、4つの組み合わせを生成します。引数を正しく対応させるには、必ず zip を使用します。例:arguments: zip([a, b], [c, d])。

  3. 状態の分離を忘れる — Swift Testing はテストメソッドごとに新しいテストインスタンスを作成します。ただし、テスト間で共有される状態(static 変数やシングルトン)は引き続き漏れます。依存性注入を使うか、テスト間でシングルトンをクリーンアップしてください。

  4. 並列テストの競合 — Swift Testing はデフォルトでテストを並列実行します。共有ファイル、データベース、シングルトンにアクセスするテストは互いに干渉します。.serialized または分離戦略を使用してください。

  5. async を自然に使わない — 非同期処理を Task { } でラップすると、本来の利点が失われます。テスト関数のシグネチャで async/await を直接使用します。例:@Test func testAsync() async throws { }。

  6. Confirmation の誤用 — confirmation はコールバックが呼び出されたことを検証するためのものです。アサーションに使うのは誤りです。アサーションには #expect を、コールバックの呼び出し回数には confirmation を使用します。

コメント