ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-07-14上級

AdMob を載せた画面のスクリーンショットテストを、広告の非決定性から守る

広告つきの画面はスクリーンショットテストが毎回赤くなり、やがて誰も差分を見なくなります。AdMob バナーの非決定性を封じ、レイアウトの崩れだけを検知に残す設計を、Compose の実装例と Antigravity での差分トリアージまで含めてまとめます。

Antigravity330AdMob13スクリーンショットテストJetpack Compose6Android27

プレミアム記事

個人開発で長く保守している壁紙アプリの設定画面に、AdMob バナーを一枚だけ置いています。ある晩、その画面のスクリーンショットテストを整えようとして、手が止まりました。同じコード、同じ端末設定、同じロケール。それなのに、実行するたびにテストが赤くなるのです。差分ビューアを開くと、変わっていたのは広告クリエイティブの中身だけでした。

最初は「たまたま」と思い、無視することにしました。ところが、そのうち本当に見るべきレイアウトの崩れが混ざり込んでも、私はもう差分ビューアを開かなくなっていました。狼が来たと叫び続けたテストは、狼が来た日に信じてもらえません。広告つきの画面を守るには、まず広告そのものを検知の外へ追い出す必要があります。

なぜ広告つきの画面は毎回赤くなるのか

スクリーンショットテストは、描画結果をピクセル単位で基準画像と比べます。ここに、開発者が制御できない要素が一つでも混ざると、テストは本質的に非決定的になります。AdMob バナーは、その最たるものです。

広告の中身はサーバーが毎回決めます。読み込みのタイミングによっては、キャプチャ時にまだ空だったり、途中まで描かれていたりもします。表示される高さも、クリエイティブによって数ピクセル揺れることがあります。次の表は、広告つき画面に潜む非決定性の要因を整理したものです。

要因症状封じ方の方針
クリエイティブの内容毎回まったく違う画像・文言が入るテスト時は広告を描かず固定プレースホルダーへ差し替える
読み込みタイミング空・半分・完了で結果が変わるそもそも実広告を読み込ませない
実測高さの揺れ数ピクセルのずれで周囲がずれるスロットの高さを固定し、レイアウトを契約として検証
ネットワーク依存CI で読み込み失敗し結果が不安定テストをネットワークから切り離す

要点は一つです。広告を「うまく比較する」のではなく、「比較の対象から外す」。そこを起点に設計します。

広告の描画方針をコードから切り離す

いちばん効いたのは、広告を描くかどうかを画面のコードに埋め込まず、外から差し替えられるようにしたことです。Compose なら CompositionLocal がちょうど良い道具でした。画面は「ここに広告スロットがある」とだけ宣言し、実際に何を描くかは呼び出し側が決めます。

enum class AdRendering { LIVE, PLACEHOLDER }
 
// 既定は本番描画。テストだけがこれを差し替える
val LocalAdRendering = staticCompositionLocalOf { AdRendering.LIVE }
 
@Composable
fun AdBannerSlot(modifier: Modifier = Modifier) {
    when (LocalAdRendering.current) {
        AdRendering.PLACEHOLDER ->
            // 実バナーと同じ実測高さの箱を置く。中身は決定的
            Box(
                modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(Color(0xFFECECEC))
                    .testTag("ad_slot")
            )
 
        AdRendering.LIVE ->
            AndroidView(
                modifier = modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .testTag("ad_slot"),
                factory = { ctx ->
                    AdView(ctx).apply {
                        setAdSize(AdSize.BANNER)
                        // Google 公式のテスト用バナー ID。実 ID は本番設定から注入する
                        adUnitId = "ca-app-pub-3940256099942544/6300978111"
                        loadAd(AdRequest.Builder().build())
                    }
                }
            )
    }
}

画面側は、広告があることを知りつつも、その中身には関与しません。

@Composable
fun SettingsScreen(state: SettingsState) {
    Column {
        SettingsHeader(title = "設定")
        SettingsList(items = state.items)
        AdBannerSlot()               // 何を描くかは LocalAdRendering が決める
        SettingsFooter(version = state.version)
    }
}

この一手で、テストは「広告を待つ」も「広告を比べる」もしなくなります。描かれるのは常に同じ灰色の箱だけです。広告 SDK をテスト用に差し替えるライブラリを探すより、描画の分岐点を一つ設けるほうが、依存も設定も増えずに済みました。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
広告差分による誤検知を消すため、広告の描画方針を CompositionLocal で切り離す設計と、その Compose 実装例
ピクセル一致をやめてレイアウト契約を検証に回す判断基準と、高さ・クリップ・重なりを assert する具体的なテストコード
スクリーンショット差分を Antigravity のエージェントに任せるときの、広告領域を除外してトリアージさせる指示の書き方
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-06-26
埋め込みエミュレータでは緑なのに、実機で初めて崩れる — AI 生成 Compose アプリに実機差を塞ぐ検証ゲートを置く
AI Studio はテキストから Kotlin/Compose アプリを生成し、埋め込みエミュレータで動かし、USB で実機へ送るところまで一画面でつなぎました。けれどエミュレータで通った画面が、手元の実機で初めて崩れることがあります。個人開発で複数アプリを抱える立場から、生成物に実機差を塞ぐ検証ゲートを据える設計をまとめました。
アプリ開発2026-06-21
Play Store の密度分割で、特定の端末だけ内蔵壁紙が消えた — drawable と nodpi の境界設計
App Bundle の密度分割は、密度別に分けてはいけない画像まで分けてしまうと、特定の密度バケットの端末からだけリソースを取りこぼします。bundletool で再現し、drawable-nodpi への移動か密度分割の無効化で直すまでを、判断基準ごと残しました。
アプリ開発2026-07-06
端末を熱くしないライブ壁紙 — WallpaperService の描画ループを電力予算から設計する
ライブ壁紙は常時描画のせいで電池を削りがちです。可視性・アイドル・熱状態の3軸で描画を止める WallpaperService.Engine の設計と、適応フレームレート制御による消費電力の実測を、個人開発の壁紙アプリの文脈でまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →