Swift開発におけるテストの順序
Swiftでモバイルアプリを開発する場合、テストは次の2段階に分けます。
本記事では、モバイルアプリ単体のテストを扱います。
- モバイルアプリ単体のテスト
- サブシステムと接続するテスト
モバイルアプリ単体のテストの実施順序
| 順序 | テスト | 接続範囲 | 主な確認内容 |
|---|---|---|---|
| 1 | 静的検査 | Swiftコードのみ | コンパイル、警告、Lint、並行処理の問題 |
| 2 | 単体テスト | クラス・関数単位 | 業務ロジック、入力検証、データ変換 |
| 3 | View・コンポーネントテスト | SwiftUI View単位 | 状態ごとの表示、入力、操作 |
| 4 | アプリ内結合テスト | アプリ内部 | View、Model、UseCase、Repositoryの連携 |
| 5 | UIテスト | アプリ+モックAPI | 主要な画面遷移と操作フロー |
| 6 | API契約テスト | アプリとAPI仕様 | リクエスト・レスポンス形式の一致 |
モバイルアプリ単体で行うテスト段階では、バックエンドAPIや認証基盤をモック、Stub、Fakeへ置き換えます。
モバイルアプリ単体完了
静的検査
→ 単体テスト
→ Viewテスト
→ アプリ内結合テスト
→ モックAPI UIテスト
1. 静的検査
アプリを実行する前に、コード上の問題を検出します。
| 検査 | 確認内容 | 主な手段・公式情報 |
|---|---|---|
| コンパイル検査 | 型、構文、APIの使い方が正しいか | Swiftコンパイラ、Xcode Build |
| コンパイラ警告 | 未使用変数、到達不能コード、非推奨APIなど | Xcode Build Settings |
| 並行処理検査 | データ競合、Sendable、Actor境界の違反 |
Swift Concurrency |
| 静的解析 | メモリや制御フローに関する潜在的不具合 | Xcode Analyze/Clang Static Analyzer設定 |
| コーディング規約検査 | 命名、複雑度、強制アンラップなど | SwiftLint |
| フォーマット検査 | インデントや改行などの書式差異 | SwiftFormat |
| 依存関係検査 | パッケージのバージョン固定と既知の脆弱性 | Swift Package Manager、GitLab Dependency Scanning |
| 独自ルール検査 | プロジェクト固有の禁止事項 | SwiftLint Custom Rules、シェルスクリプト |
2. 単体テスト
関数や型の振る舞いを、UIや通信から切り離して確認します。新規開発ではSwift Testing、既存資産との互換性が必要な場合はXCTestを使用します。
| テストフレームワーク | 短い説明 | 主な用途 |
|---|---|---|
| Swift Testing |
@Testや#expectを使う、Swift向けの新しいテストフレームワークです。 |
新規開発の単体テスト・結合テスト |
| XCTest | Apple製アプリで長く使われている標準テストフレームワークです。 | 既存テストの継続、性能テスト、XCUITestによるUIテスト |
Swift Testingは、2024年6月のWWDC24で発表され、2024年9月に正式版のXcode 16/Swift 6とともに利用可能になりました。
単体テストの主な対象は次のとおりです。
- 入力チェック
- 金額や日付の計算
- データ変換
- 状態遷移
- エラー判定
- ViewModelや状態管理クラス
- UseCaseなどの業務処理
3. View・コンポーネントテスト
SwiftUIのView(画面表示)が、与えられた状態に対して正しく表示されるか確認します。
対象は次のとおりです。
- 初期状態
- 読み込み中
- データ取得成功
- データが0件
- 入力エラー
- 通信エラー
- 長い文字列
- Dynamic Type
- ダークモード
#Preview("エラー") {
LoginView(
state: .error("ログインに失敗しました")
)
}
ただし、Previewはテストの代わりにはなりません。Previewは表示確認を速める手段であり、期待結果を自動判定しないためです。
ロジックは単体テスト、重要な操作はUIテスト、見た目はPreviewと必要に応じた画像比較で確認します。
4. アプリ内結合テスト
複数の部品を接続し、データが正しく流れることを確認します。
アプリ内結合テストに含める範囲は次のとおりです。
含める
├── View
├── 画面Model
├── UseCase
├── Repository
├── API Client
├── DTO・Domain変換
├── Local Store
├── Token管理
└── Router
モックへ置き換える
├── バックエンドAPI
├── 外部認証基盤
├── Push通知サーバー
├── 外部ストレージ
└── 他サブシステム
優先度が最も高い接続点は、Model → UseCase → Repository → API Client → DTO → Domain Model → Repository → UseCase → Modelのフローです。
この経路を通すことで、画面から発生した要求がHTTPリクエストへ変換され、レスポンスが画面状態へ戻るまでのアプリ内部のデータフローを検証できます。
5. UIテスト
アプリを利用者と同じように操作して、主要な業務フローを確認します。通常はXCUITestを使用します。
XCUITestとは
iPhoneやiPadアプリを利用者と同じように操作し、画面の振る舞いを自動確認するApple標準のUIテスト機能です。
XCTestの一部として提供され、Xcodeから実行します。
import XCTest
final class LoginUITests: XCTestCase {
func testLoginSuccess() {
let app = XCUIApplication()
app.launch()
app.textFields["メールアドレス"].tap()
app.textFields["メールアドレス"].typeText("user@example.com")
app.secureTextFields["パスワード"].tap()
app.secureTextFields["パスワード"].typeText("password")
app.buttons["ログイン"].tap()
XCTAssertTrue(app.staticTexts["ホーム"].waitForExistence(timeout: 5))
}
}
UIテストは、すべての入力パターンを網羅する場所ではありません。次のような重要経路に絞ります。
- ログインから主要画面への遷移
- 登録・更新・削除
- 検索
- エラーからの復帰
- アプリ再起動後の状態
- 重要な権限許可・拒否
細かい条件分岐は単体テストで網羅します。UIテストを増やしすぎると、実行時間と不安定性が増えます。
