iOSアプリでログイン後のコールバック、キャンペーンページ、注文詳細、通知からの遷移先をディープリンクに任せていると、ルーティングを一度リファクタリングしただけで、リンクを開いてもホーム画面に留まったり、アプリが起動済みのときに同じ画面が重複して積まれたりすることがあります。リンクをいくつか手動でタップするだけでは、その時点で動作したことしか確認できず、コールドスタート、フォアグラウンド状態、エンコードされたパラメータ、不正な入力までは網羅できません。より確実なのは、クラウドMac上でシミュレータ環境を固定し、各ディープリンクの最終的なルーティング結果を継続的インテグレーションから判定できるようにする方法です。
まずリンクをレビュー可能な契約として定義する
テストスクリプト内でURLを場当たり的に組み立ててはいけません。まず、入力、期待するルート、アプリの状態を明記したルーティング一覧を管理します。
| シナリオ | 入力 | 期待結果 |
|---|---|---|
| 商品詳細 | linklab://product/42 |
product/42 |
| 検索パラメータ | linklab://search?q=swift%20ui |
search?query=swift ui |
| 識別子なし | linklab://product/ |
error/invalid-product |
| 未知のパス | linklab://unknown |
error/not-found |
一覧に記載する期待値は、特定のボタン名ではなく、ルーターが解析した後の正規化済み形式にします。これにより、画面上の文言変更による誤検知を避けられ、問題がパス、パラメータ、状態復元のどこにあるのかをテスト結果から直接判断できます。
ディープリンクのゲートで検証するのは「入力が正しい業務ルートに到達したか」であり、「システムがURLを受け付けたか」ではありません。コマンドが成功しても、正しい画面が表示されたとは限りません。
同じルートについて、少なくともコールドスタート時とフォアグラウンド時の2ケースを用意します。認証が関係する場合は、ログイン済みと未ログインも分けます。ただし、テストアカウントの状態はスクリプトで明示的に構築し、直前のテストケースから引き継いではいけません。
デバッグビルドに観測用プローブを追加する
コマンドラインからリンクを開くことはできても、アプリが最終的に何を表示したかを確実に判定することはできません。DEBUG ビルドでは、共通のルーティング入口から正規化済みの結果をキャッシュディレクトリへ書き出すようにします。リリースビルドにはこのロジックを含めず、トークンや完全なクエリパラメータも記録しないでください。
#if DEBUG
func recordResolvedRoute(_ route: String) {
let payload: NSDictionary = [
"route": route,
"recordedAt": Date().timeIntervalSince1970
]
let directory = FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
let file = directory.appendingPathComponent("link-probe.plist")
payload.write(to: file, atomically: true)
}
#endif
プローブは、URL Scheme、Universal Link、通知からの遷移が共通して通るルーターの後段に配置します。ライフサイクルコールバックごとに個別に記録すると、テストで確認できるのはコールバックが呼ばれたことだけになりかねません。パラメータが検証され、実際の業務画面へ正しく到達したことまでは証明できません。
結果をアトミックに保つ
実行前に毎回古いファイルを削除し、書き込みにはアトミックな置換を使用します。そうしないと、アプリのクラッシュや早すぎる読み取りによって、今回のテストが前回の結果を誤って参照する可能性があります。記録するのはルートキー、エラー種別、タイムスタンプだけに絞り、ユーザー入力は事前にマスキングすることを推奨します。
シミュレータを固定してルーティングマトリクスを実行する
CIでは、起点として曖昧な booted を使用しないでください。まずデバイス名とOSバージョンからUDIDを特定し、対象のシミュレータだけが起動していることを確認してから、システムの起動完了を待ちます。以下では、パイプラインですでに SIMULATOR_UDID を取得済みであるものとします。
set -euo pipefail
xcrun simctl shutdown all
xcrun simctl boot "$SIMULATOR_UDID"
xcrun simctl bootstatus "$SIMULATOR_UDID" -b
xcodebuild \
-scheme LinkLab \
-configuration Debug \
-sdk iphonesimulator \
-derivedDataPath .ci/DerivedData \
build
APP_PATH=".ci/DerivedData/Build/Products/Debug-iphonesimulator/LinkLab.app"
xcrun simctl install "$SIMULATOR_UDID" "$APP_PATH"
インストール後は、まずデータコンテナを取得します。その後、ケースごとにプローブを削除し、リンクを開いて結果をポーリングします。長い固定時間の sleep は使用しないでください。アプリの起動時間が変わると、無駄に待つか、それでもファイルを読み取れないかのどちらかになります。
BUNDLE_ID="com.example.linklab"
DATA_DIR="$(xcrun simctl get_app_container \
"$SIMULATOR_UDID" "$BUNDLE_ID" data)"
PROBE="$DATA_DIR/Library/Caches/link-probe.plist"
rm -f "$PROBE"
xcrun simctl openurl "$SIMULATOR_UDID" "linklab://product/42"
for attempt in 1 2 3 4 5 6 7 8 9 10; do
test -f "$PROBE" && break
sleep 1
done
test -f "$PROBE"
ACTUAL="$(/usr/libexec/PlistBuddy -c "Print :route" "$PROBE")"
test "$ACTUAL" = "product/42"
フォアグラウンドのケースでは、最初にアプリを開いた後、そのままURLを送信します。コールドスタートのケースでは、先に simctl terminate を実行します。失敗した各ケースについて、入力、期待値、実際の値、テストログを保存します。ただし、機密情報を含む完全なアプリコンテナを成果物として保存してはいけません。
Universal Linkは2層に分けて検証する
URL Schemeはルーターを安定して検証するのに適していますが、Universal Linkのエンドツーエンド検証を代替するものではありません。Universal Linkは、Associated Domainsの権限、HTTPSサイト上のファイル、コンテンツタイプ、アプリ識別子、システムキャッシュにも依存します。
第1層では、コミットごとに共通ルーターを直接呼び出し、パスの正規化、パーセントエンコーディング、重複パラメータ、空値、未知のルートを高速に網羅します。第2層では、管理されたテストドメインで実際のHTTPSリンクを使用し、アプリを再インストールしたうえでコールドスタートとフォアグラウンドへの切り替えを検証します。サイトの関連付けファイルを変更した場合は、必ず第2層を実行し、単体テストだけで済ませてはいけません。
よくある誤りは、関連付けファイルを変更した直後にリンクを繰り返しタップし、古いキャッシュの結果を新しい設定によるものと判断することです。調査時には、アプリのビルド番号、OSバージョン、インストール順序、テストドメインのレスポンスヘッダーを記録します。まずシステムがリンクをアプリへ渡したことを確認し、その後で業務ルートを検証してください。
失敗時の証跡とクリーンアップをゲートに組み込む
信頼できるゲートは、非ゼロのステータスを返すだけでなく、保守担当者が障害の層を素早く特定できるようにする必要があります。失敗の要約には、少なくともテストケース名、アプリ状態、入力種別、期待ルート、実際のルート、プローブファイルが生成されたかどうかを含めます。ファイルが存在しない場合は、インストール、ライフサイクルの入口、リンク宣言を優先して確認します。ファイルは存在するもののルートが誤っている場合は、パーサーとナビゲーション状態を確認します。
マージ前のチェックリストは、次のように簡潔にまとめられます。
- シミュレータのモデルとOSバージョンがパイプラインで固定されている。
- コールドスタートとフォアグラウンド状態がそれぞれ実行される。
- 各テストケースの前に、アプリプロセスまたはプローブの状態がリセットされる。
- 特殊文字、空パラメータ、未知のパスに対するネガティブケースがある。
- Universal Linkには独立したHTTPS検証がある。
- 失敗時の成果物に認証情報、トークン、ユーザーデータが含まれない。
- ジョブ終了後にシミュレータを停止し、一時ビルドディレクトリを削除する。
最後に xcrun simctl shutdown "$SIMULATOR_UDID" を実行し、ワークスペースのクリーンアップポリシーで .ci/DerivedData を処理します。ルーティング契約、観測可能なプローブ、状態の分離がすべて揃って初めて、ディープリンクテストは単なる「リンクが開く」確認から、ナビゲーションの回帰を阻止できるエンジニアリングゲートへと進化します。
よくある質問
simctl openurlだけでUniversal Linkを完全に検証できますか?
できません。アプリ内ルーティングに加え、HTTPSの関連付けファイル、署名済み権限、TLS配信、システムキャッシュを別工程で確認します。
コールド起動と前面表示の両方をテストする理由は何ですか?
異なるライフサイクルコールバックを通る可能性があるためです。両方を試すことで初期ルートの欠落や二重遷移を検出できます。
ビルド、テスト、実験に専用物理ノードを選択
2種類のM4構成を、シンガポール、東京、ソウル、香港のノードで提供しています。実際の利用可否は、注文時に返される結果をご確認ください。