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

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

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

Antigravity386AdMob13スクリーンショットテスト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-05-25
Antigravity の Inline Edit と Agent モードを壁紙アプリ運用で1ヶ月使い分けた所感
壁紙アプリ4本の実運用で Antigravity の Inline Edit と Agent モードを1ヶ月使い分けた所感です。実測値・AGENTS.md への判断軸の固定・dSYM 一括統一・クレジット消費まで、現実的な運用設計を共有します。
▣ アプリ開発2026-05-21
Firebase Remote Config と Antigravity Agent で AdMob 配置を実運用しながら微調整する設計
壁紙アプリの AdMob 収益が頭打ちになった経験から、Firebase Remote Config と Antigravity Agent を組み合わせて広告配置を実運用中に少しずつ最適化する仕組みをまとめます。
▣ アプリ開発2026-05-10
古いアプリの保守をAntigravityに任せて、手元に残した三つの判断
長く運用している壁紙・癒し系アプリの保守を、Antigravityにどこまで任せられたか。クラッシュ修正と段階公開の実録をもとに、任せた作業・手元に残した作業の線引きと、文脈メモの書き方を共有します。