ヴィジュアルリグレッションテスト(VRT)導入
ヴィジュアルリグレッションテスト(VRT)導入
方針: A = swift-snapshot-testing(Point-Free)で決定論的スナップショットを CI ゲート化。
固定環境: iPhone 17 Pro / iOS 26.1(記録・検証は同一機種・OS で行うこと) ※ iPhone 16 無印は OS 26.1 シミュレータ未インストールのため不可。実機 iPhone 17 Pro に一致させた。
タスク
- テスト構成の調査(pinNestTests はスタンドアロン同期ターゲット)
- スナップショット共通ヘルパー作成(
pinNestTests/Snapshot/SnapshotSupport.swift) - PinCardView スナップショットスイート作成(
pinNestTests/Snapshot/PinCardSnapshotTests.swift) - Xcode で
swift-snapshot-testingをpinNestTestsターゲットへ追加(1.19.2) - 参照画像を記録(12 枚)し検証パス(要
__Snapshots__/をコミット) - 比較モードで再実行し全パス確認(決定論性の検証)→
** TEST SUCCEEDED ** - 対象 View を拡大(PinList / History / PinDetail / Settings をフルスクリーンで追加 = 8 枚)
※
EmptyReducer+ 固定 State +.device(config: .iPhone13Pro)方式。glassEffect は ContentView 側のみで各画面には無し - CI ワークフロー追加(
.github/workflows/vrt.yml、GitHub-hosted / main への PR) - CI 参照画像を runner(26.2) 基準で記録・コミット → verify green 確認済み(10 tests / 20 枚 pass)
CI 構成(.github/workflows/vrt.yml)— 確定
- runner:
macos-26/ Xcode 26.2 / iOS 26.2 / iPhone 17 Pro - トリガー:
pull_request(main) で検証 /workflow_dispatch(mode=record) で参照記録 - 失敗時は
TestResults.xcresult(reference/取得/差分画像つき)を artifact 化 - 参照画像は runner(26.2) 基準(ローカル26.1とは別)。View 追加時は
mode=record→ artifactrecorded-snapshotsを差し替えコミット - 必須フラグ:
-skipMacroValidation/-enableCodeCoverage NO/-parallel-testing-enabled NO - record の env は
TEST_RUNNER_SNAPSHOT_TESTING_RECORD=all(testmanagerd 経由のため) - ダミー
GoogleService-Info.plistを CI で生成(gitignore のため) - 詳細な落とし穴は
lessons.md/ メモリvrt-snapshot-testing.md参照
実行コマンド(確定)
記録(撮り直し時):
SNAPSHOT_TESTING_RECORD=all xcodebuild test \
-project /ABS/PATH/pinNest.xcodeproj -scheme pinNest \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.1' \
-only-testing:pinNestTests/PinCardSnapshotTests -parallel-testing-enabled NO
検証(CI / 通常):
xcodebuild test \
-project /ABS/PATH/pinNest.xcodeproj -scheme pinNest \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.1' \
-only-testing:pinNestTests/PinCardSnapshotTests -parallel-testing-enabled NO
※ -parallel-testing-enabled NO は Clone 並列による参照ファイルレース回避のため必須。
レビュー
PinCardView× 5 タイプ + favorite × light/dark = 12 スナップショット。全パス。.sizeThatFitsではサムネイル(Color+aspectRatio)が高さ 0 に潰れる問題を、 幅固定 + 高さ実測 +.fixed方式で解決(SnapshotSupport参照)。詳細はlessons.md。
手動手順(SPM パッケージ追加)
- Xcode で
pinNest.xcodeprojを開く - File > Add Package Dependencies…
- URL:
https://github.com/pointfreeco/swift-snapshot-testing - Dependency Rule: Up to Next Major Version(1.x)
- Add Package → プロダクト
SnapshotTestingをpinNestTestsターゲットに紐付け - 追加後、
SnapshotSupport.swiftのimport SnapshotTestingエラーが解消されることを確認
決定論性メモ
- PinCardView は相対日付を描画しない → Pin を固定すれば決定論的
- サムネイルは filePath = nil でプレースホルダー分岐 → ディスク依存なし
.ultraThinMaterial(blur)は perceptualPrecision 0.98 で許容- glassEffect を使う floating bar / FAB は非決定論リスクが高いため後回し