markdown

はじめに (対象読者・この記事でわかること)

本記事は、iOS アプリや macOS アプリの開発において XCTest を使った単体テストを書いている方を対象としています。特に、setUp() がテストケースごとに自動的に呼び出されないという現象に遭遇したことがある方に向け、原因の切り分け方から実装上のベストプラクティスまで網羅的に解説します。この記事を読むことで、以下を実現できるようになります。

  • setUp() が呼び出されない典型的なパターンを把握できる
  • Xcode の設定やテストターゲットの構成ミスを特定できる
  • 正しい setUp() の記述方法と、代替手段(setUpWithError など)を使いこなせる

テストが期待通りに動作しないと、バグの検出漏れやデバッグ時間の増大につながります。本記事は、そのようなフラストレーションを解消するための実践ガイドです。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • Swift の基本的な文法とクラス/構造体の概念
  • Xcode でのプロジェクト作成とテストターゲットの追加方法
  • XCTest フレームワークの基礎(XCTestCase、テストメソッドの命名規則など)

問題の概要と原因の考察

XCTestCase のサブクラスに override func setUp() を実装しても、テスト実行時にブレークポイントがヒットしない、あるいは初期化処理が走らないというケースは意外と頻繁に報告されています。まずは「呼び出されない」現象を正しく捉えることが重要です。

よくある原因一覧

カテゴリ 具体例 影響
シグネチャミス func setUp()override なし、throws なし) オーバーライドが認識されず、スーパークラスの空実装が呼ばれる
テストクラスの属性 final 修飾や private メソッド化 XCTest がリフレクションで検出できない
ターゲットの設定 テストターゲットに XCTest がリンクされていない、もしくは Enable Testability がオフ ビルドは通るがランタイムでメタデータが欠如
Xcode のバグ 特定バージョンで setUpWithError を使うと setUp がスキップ コンパイラ最適化が影響し、-O ビルドで発生
非同期テスト async テストメソッド内で await 前に setUp が呼ばれない 実行順序の混乱が原因

なぜシグネチャが重要なのか

XCTestCasesetUp() は次のシグネチャで定義されています。

Swift
override func setUp() { super.setUp() // ここに共通初期化コードを書く }

この override がない、もしくは throws を付与した setUp() とすると、Swift コンパイラは別メソッドとして扱い、テストランナーはスーパークラスの空実装を呼び出すだけになります。その結果、実装したロジックは一切実行されません。

解決策と実装例

以下では、実際に「setUp が呼び出されない」問題を再現し、ステップごとに解決策を示します。コード例は macOS 13 / Xcode 15.2 を前提にしていますが、概ね他バージョンでも同様です。

ステップ1:シグネチャを正しく記述する

Swift
import XCTest final class MyFeatureTests: XCTestCase { // ✅ 正しいオーバーライド override func setUp() { super.setUp() // テスト共通前提条件を設定 Database.shared.clearAll() } // ❌ 失敗例(override が抜けている) // func setUp() { … } }

ポイント

  • override を必ず付与する
  • super.setUp() を先頭で呼び出す(これがないと内部状態が初期化されない)

ステップ2:setUpWithError への置き換え

Swift 5.3 以降、setUpWithError() が導入されました。エラーを投げられるのでテスト前処理が失敗したときに明示的に失敗させられます。

Swift
override func setUpWithError() throws { try super.setUpWithError() // 例外が投げられる可能性がある処理 try Configuration.load() }

活用シーン
ファイル I/O やネットワーク接続の初期化が必要なテストで、失敗時にテスト全体をスキップしたい場合に有効です。

ステップ3:テストターゲットの設定確認

  1. プロジェクト Navigator でテストターゲットを選択
  2. Build Phases → Link Binary With LibrariesXCTest.framework が含まれているか確認
  3. Build Settings → Enable TestabilityYES になっているか確認(特に Debug ビルド構成)

不足がある場合は、手動で追加・ON にしてください。これだけで setUp が正しく呼び出されるケースが多いです。

ステップ4:非同期テストでの注意点

async テストメソッドを利用している場合、setUp同期 で実行される点に留意します。非同期処理は setUp 内で await できませんが、代わりに setUp で同期的に Task {} を起動し、テスト実行前に完了させる手法があります。

Swift
override func setUp() { super.setUp() let expectation = XCTestExpectation(description: "Async init") Task { await AsyncInitializer.shared.prepare() expectation.fulfill() } wait(for: [expectation], timeout: 5.0) }

ハマった点やエラー解決

現象 原因 解決策
setUp がブレークポイントで止まらない override が抜けている override を付与し、super.setUp() を呼ぶ
ビルドは通るがテストが XCTFail になる Enable Testability が OFF Build Settings で有効化
setUpWithError でコンパイルエラー Xcode 13 以下 では未実装 setUp に戻すか、Xcode バージョンを更新
非同期テストで setUp が完了しない wait が無い XCTestExpectation を用いて同期させる

完全サンプルプロジェクト

以下は GitHub に公開しているミニプロジェクトです。README にはセットアップ手順と、各テストケースの期待結果が記載されています。

  • Repository: https://github.com/Kousukei/XCTestSetUpIssueDemo
  • 主なファイル構成
  • MyFeatureTests.swift(上記コードがすべて入っている)
  • AsyncInitializer.swift(非同期初期化ロジック)
  • Database.swift(テスト用インメモリ DB)

このリポジトリをクローンし、Product → Test で実行すると、すべての setUp が正しく呼び出されることが確認できます。

まとめ

本記事では、XCTest において setUp() が呼び出されない典型的な原因と、具体的な対策を体系的に解説しました。

  • シグネチャミス が最も多く、overridesuper.setUp() の忘れが根本原因
  • テストターゲット設定(XCTest のリンク、Enable Testability)の確認が必須
  • setUpWithError を活用すればエラーハンドリングがシンプルになる
  • 非同期テスト では XCTestExpectation を用いた同期処理が鍵

これらのポイントを抑えることで、テストの前処理が確実に実行され、信頼性の高いユニットテストを構築できます。次回は、tearDown() のカスタマイズやテストデータの自動生成について掘り下げた記事を予定しています。

参考資料