Golden Test(スナップショットテスト、リファレンステスト)— UIのビジュアルテスト手法で、現在のコンポーネントレンダリングを事前に保存されたリファレンス画像(goldenファイル)と比較します。ピクセルの変更が設定されたしきい値を超えると、テストは失敗し、diff画像を生成します。開発者はdiffを確認し、変更を受け入れる(goldenを更新する)か、バグを修正します。詳細はPaparazziに関するMeta Engineeringの記事をご覧ください。
重要なポイント
Golden Test — コンポーネントの外観をリファレンスとピクセル単位で比較する自動チェックです。プロセス:(1) 開発者またはテスターがコンポーネントの最初のスナップショットを作成します — これが“golden”(リファレンス)です。(2) goldenファイルはテストの隣のリポジトリに保存されます。(3) 後続の実行では、テストがコンポーネントを再レンダリングし、保存されたgoldenと比較します。(4) 画像が一致すれば — テストは成功。異なれば — テストは失敗し、diffが表示されます。決定:変更が予期されている(goldenを更新する)か、バグです。
Goldenの生成方法 — ライブラリは実際のディスプレイなしでコンポーネントをオフスクリーンバッファ(Android: Canvas、iOS: UIGraphicsImageRenderer)にレンダリングします。つまり、goldenテストは画面エミュレーター(仮想ディスプレイ)なしでCIで動作し、実行が高速化されます。AndroidのPaparazziはAndroid StudioのLayoutlib — Layout Editorと同じエンジン — を使用します。iOSSnapshotTestCaseはCGImageへのUIKitレンダリングを使用します。結果は固定サイズのPNGファイルです。
Goldenファイル — 1画面(1080x1920)のPNGスナップショットは、複雑さに応じて200–800KBを占めます。500のgoldenテストがあるプロジェクトでは、リポジトリに約100–400MBになります。解決策:(1) goldenをGit LFSに保存する。(2) PNG圧縮(pngcrush、oxipng)を使用する。(3) goldenを別のストレージ(S3)に保存し、ビルド時に取得する。IT Sectrでは、1ファイルあたり1MBのしきい値でGit LFSにgoldenを保存しています — これは90%のテストで十分です。
Flaky goldenテスト — goldenテストの主な問題。異なるGPU、フォントバージョン、アンチエイリアシングにより、ピクセルに微細な差が生じます。解決策:しきい値(許容される異なるピクセルの割合)、ファジー比較、および同一のCIエージェント(同じGPU、OS、エミュレーターバージョン)での実行。Paparazziはピクセルパーフェクトな比較を使用するため、CIエージェントは同一である必要があります。
Golden Test — 固定リファレンスを使用するスクリーンショットテストの一種です。“golden”という用語は、リファレンスがチームによって承認され、リポジトリに保存されていることを意味します。画像の変更には、開発者の意識的な決定(goldenの更新またはコードの修正)が必要です。Golden Testは個々のコンポーネント(Composable、UIView)レベルで機能し、実際のデバイスは必要ありません。
Screenshot Test — より広い概念です。スクリーンショットテストは、実際のデータ、ナビゲーション、システムステータスバー、アニメーションを含む全画面をキャプチャできます。スクリーンショットテストは、多くの場合、UI Automator(Android)またはXCUITest(iOS)を介して実際のデバイスまたはエミュレーターで実行されます。Goldenテストは、エミュレーターなしでユニットテスト環境(JVM、XCTest)で実行され、単一のコンポーネントのみをキャプチャします。
| 特性 | Golden Test | Screenshot Test |
|---|---|---|
| レベル | コンポーネント/Composable/View | 全画面 |
| 環境 | ユニットテスト(オフスクリーンバッファ) | デバイス/エミュレーター |
| 速度 | テストあたり50–200ms | テストあたり2–30秒 |
| アニメーション | 非対応 | 対応(一時停止あり) |
| GPUなしのCI | 動作する(Layoutlib) | エミュレーターが必要 |
| セットアップの複雑さ | 低い | 高い(エミュレーター/Device Farm) |
| 不安定性 | 中程度(異なるGPU) | 高い(エミュレーター、時間) |
Golden vs Screenshot — 各コミットで個々のUIコンポーネント(ボタン、カード、ダイアログ)を確認するためのgoldenテスト。リリース前に全画面のE2E確認を行うためのスクリーンショットテスト。Goldenテストは開発者に迅速なフィードバックを提供し、スクリーンショットテストはアプリケーション全体の整合性に自信を与えます。IT Sectrでは、Pull Requestにはgoldenテスト(3–5分)、夜間にはスクリーンショットテスト(30–60分)を使用しています。
Paparazzi — Cash App(Square)のライブラリで、Android ViewおよびJetpack ComposeコンポーネントをエミュレーターなしでPNGにレンダリングします。Layoutlib(Android Studio Previewと同じエンジン)を使用します。セットアップ:Gradleプラグインを追加し、@Testと@RunWith(PaparazziRule::class)でテストを書き、paparazzi.snapshot(view)を呼び出します。Paparazziはアニメーション、ビデオ、Real Deviceをサポートしません — 静的コンポーネントのレンダリングのみです。
// build.gradle.kts (module)
plugins {
id("app.cash.paparazzi") version "1.3.1"
}
// ComposeコンポーネントのGoldenテスト
class ButtonGoldenTest {
@get:Rule
val paparazzi = Paparazzi(
Paparazzi.PaparazziSnapshotConfig(
deviceConfig = DeviceConfig.PIXEL_6,
theme = "android:Theme.Material.Light.NoActionBar"
)
)
@Test
fun primary_button() {
paparazzi.snapshot {
Button(
onClick = { },
modifier = Modifier.width(200.dp)
) {
Text("Submit")
}
}
}
}
Roborazzi — Compose、View、画像比較をサポートするPaparazziの代替。違い:RoborazziはRobolectricを介して動作し、しきい値(許容ピクセル差の割合)をサポートします。これにより、CI上の異なるGPUでの不安定性が軽減されます。Roborazziは変更のGIFアニメーション(前/後/diff)も作成でき、コードレビューに便利です。ファイル形式:PNG + JSONメタデータ。
Goldenの更新 — 意図的なUI変更後、開発者は古いgoldenファイルを削除し、recordフラグを付けてテストを実行します。Paparazziはすべてのgoldenファイルを再作成します。その後、開発者はコード変更とともに新しいgoldenファイルをコミットします。コードレビューでは、レビュアーは古いgoldenと新しいgoldenのdiffを確認します。変更が承認されれば — PRがマージされます。承認されなければ — 開発者はコードを修正し、テストを再実行します。CIでgoldenファイルを自動的に更新しないでください — ローカルでのみ行ってください。
SwiftSnapshotTesting — pointfree.coのライブラリ、Composable Architectureの作成者。UIView、UIViewController、CALayer、SwiftUI Viewをサポート。原理:assertSnapshot(matching: view, as: .image)。初回実行時にgoldenが自動的に作成されます。後続の実行で比較されます。差が許容しきい値を超えると、テストは失敗します。SwiftSnapshotTestingはUIGraphicsImageRendererを介して動作し、CI(Xcode Cloud、GitHub Actions)と互換性があります。
import SnapshotTesting
import XCTest
final class ProfileCardSnapshotTests: XCTestCase {
func test_profile_card_default() {
let card = ProfileCard(
name: "Alice",
avatar: UIImage.testImage(),
badge: "Pro"
)
let controller = UIHostingController(rootView: card)
assertSnapshot(
matching: controller,
as: .image(on: .iPhoneSe),
record: ProcessInfo.processInfo
.environment["RECORD"] != nil
)
}
}
iOSSnapshotTestCase(旧FBSnapshotTestCase) — UberのUIKit向けライブラリ。SwiftSnapshotTestingとは異なり、iOSSnapshotTestCaseは画面サイズと向きの指定が必要です。GoldenファイルはReferenceImagesフォルダのPNGです。利点:SwiftUIなしでUIKitと連携し、iOS 12+をサポート。欠点:goldenを自動更新しない — recordフラグを付けて実行する必要があります。SwiftSnapshotTestingはより現代的で、新しいプロジェクトに推奨されます。
デバイス固有のgolden — 画面サイズや向きによってgoldenファイルは異なります。標準的なアプローチ:goldenファイルをTestName@3x~iPhone14.pngのように命名します。SwiftSnapshotTestingは、.image(on: .iPhoneSe)パラメータが指定されている場合、自動的にデバイスサフィックスを追加します。Androidでは、PaparazziはDeviceConfigを使用してサイズを設定します。サポートされている各デバイスのフォームファクターごとにgoldenを個別に保存してください。異なるサイズに1つのgoldenを使用しないでください — 不安定なテストになります。
CIパイプライン — goldenテストはすべてのPull Requestで実行する必要があります。テストが失敗すると、CIはdiff画像をビルドアーティファクトとして表示します。開発者はdiffを確認して決定を下します。重要:CIで生成されたgoldenファイルは自動的にコミットされることはありません。意図的な変更後に開発者がローカルでのみ生成します。GitHub ActionsとGitLab CIは、ブラウザでdiffを表示するためのアーティファクト(png、html)のアップロードをサポートしています。
リポジトリサイズ — goldenファイルは急速に増加します。500テスト = 100–400MBのPNG。解決策:(1) Git LFS — 各goldenはLFSに保存され、チェックアウト時のみクローンされます。(2) goldenを別のリポジトリに保存し、サブモジュールとして含める。(3) S3 + キャッシュ — goldenをS3に置き、CIはチェックサムで変更されたファイルのみをダウンロードします。IT Sectrでは、track *.png filter=lfs diff=lfs merge=lfs text=falseでGit LFSを使用しています。ローカルでは、goldenはsrc/test/goldens/にあります。
Goldenのコードレビュー — 通常のgit diffはPNGの変更を表示しません。解決策:(1) GitHubはクリックでPNG画像を開きます。(2) golden diffがブラウザで表示されるReview Appsを使用する。(3) 前/後/diffの列を持つHTMLレポートを生成する。Paparazziは3列(actual、expected、diff)のHTMLレポートを作成します。レポートはCIアーティファクトに添付されます。レビュアーはファイルをローカルにダウンロードせずにレポートを確認できます。
Goldenを更新するタイミング — 意図的なUI変更後にのみ。フォント、色、パディング、アイコンの変更 — goldenを更新する必要があります。新しいボタンの追加、要素の再配置 — goldenを更新する必要があります。外観を変更するバグ修正 — goldenを更新する必要があります。UI変更のないリファクタリング — goldenは変更されるべきではありません。UIコードの変更なしにgoldenが変更された場合 — それは環境によるflakyテストであり、CIエージェントまたは依存関係のバージョンに原因を探してください。
よくある質問
Golden Test — ユニットテスト環境でのコンポーネントレベルのスナップショットテスト(高速、エミュレーター不要)。Screenshot Test — デバイスまたはエミュレーターで全画面をキャプチャ(低速だが現実的)。Goldenはオフスクリーンバッファで動作し、screenshotは実際のディスプレイで動作します。Goldenは毎回のコミットでのCIに適し、screenshotはリリース前の夜間実行に適しています。
主な原因:(1) CI上の異なるGPU — 同一のCIエージェントを使用します。(2) 異なるフォントバージョン — OSバージョンを固定します。(3) 異なるアンチエイリアシング — しきい値を設定します(Roborazzi、iOSSnapshotTestCase)。(4) アニメーション — テストでアニメーションを無効にします。(5) システム要素(ステータスバー) — ベゼルレスデバイス設定を使用します。PaparazziはLayoutlibのおかげで不安定性の影響を受けません。
はい。Paparazziはpaparazzi.snapshot { }を介したComposeの組み込みサポートを備えています。RoborazziもComposeをサポートしています。iOSでは、SwiftSnapshotTestingはUIHostingControllerを介してSwiftUIで動作します。ComposeコンポーネントはLayoutlibを介してレンダリングされ、SwiftUIはUIKitレンダリングを介してレンダリングされます。制限:ComposeおよびSwiftUIのアニメーションはサポートされていません — goldenテストは初期状態のみをキャプチャします。
CIでのgolden受け入れを自動化しないでください。ローカルのみ:開発者はディレクトリから古いgoldenファイルを削除し、recordフラグを付けてテストを実行します(Paparazzi: record=true、SwiftSnapshotTesting: record=true)。Goldenファイルが再作成されます。開発者は各goldenの正確性を確認し、コードとともに変更をコミットします。CIでの自動受け入れは、UIバグの見逃しにつながります。
Goldenテストはインストゥルメント化テスト(UI Automator、XCUITest)よりも高速です。1つのgoldenテストは50–200msで実行されます(Paparazzi: 平均的なMacBook Proで100–150ms)。500のgoldenテスト = 25–100秒。エミュレーター経由のスクリーンショットテストと比較:テストあたり5–30秒。Goldenテストはビルドを遅くしません:100テスト = 約15秒で、マージ前チェックに十分許容範囲です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。