Debugging log · AdMob
Don't ship real AdMob ad unit IDs in a closed test
When I put a few apps into closed testing, I did the obvious-seeming thing: built the release bundle with my real AdMob ad unit IDs, uploaded it, and had testers poke around. It felt safe — it's "just testing," right? It isn't. To AdMob, a tap on a live ad unit is a real click, no matter which testing track served it. That's exactly the behavior that gets accounts flagged for invalid activity.
The short version: closed-test traffic is real traffic. Never let yourself or your testers tap live ads. Use Google's demo ad unit IDs during development, and for release builds you test through Play, register the devices as test devices so they see test ads instead.
Why this matters
AdMob's whole system runs on the assumption that impressions and clicks come from genuine users. Clicks generated by you, your friends, or a tester group don't fit that, and Google's own guidance is blunt about it: if you rack up clicks on your own ads outside of test mode, you risk having your account flagged for invalid activity. For a solo developer, an AdMob suspension can take down monetization across every app at once. It is not worth risking over a testing convenience.
Use Google's demo ad units in development
Google publishes demo ad units that always return test creatives, aren't tied to your account, and generate no billable traffic — so they're safe to tap. For Android the common ones are:
Banner ca-app-pub-3940256099942544/6300978111 Interstitial ca-app-pub-3940256099942544/1033173712 Rewarded ca-app-pub-3940256099942544/5224354917
Google also provides demo units for app-open, native, and rewarded-interstitial formats, plus a sample App ID, on its official test-ads page. Ads served through these show a small Test Ad label, which is your confirmation that no real traffic is being recorded.
Before you publish, swap these back to your own ad unit IDs. Shipping demo units to production means you show test ads to real users and earn nothing.
The pattern I use now: swap by build type
To make it impossible to forget, drive the IDs off the build type so debug never touches live units. A minimal Kotlin example:
val bannerAdUnitId = if (BuildConfig.DEBUG) {
"ca-app-pub-3940256099942544/6300978111" // Google demo (test)
} else {
"ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY" // your real unit
}
Even cleaner is to put the values in buildConfigField entries per build type, so the right ID is baked in at compile time and there's no runtime branch to get wrong.
But closed testing uses a release build…
Here's the wrinkle that tripped me up. A closed test runs your release build — the same artifact you'll promote to production, with your real IDs in it. You can't just compile test IDs into it, or you'd ship test ads to production. The right move for that case is to register the test devices:
- Add your own and your testers' devices as test devices — via the AdMob UI, or programmatically with the SDK's request configuration. Emulators are treated as test devices automatically.
- With a device registered, your live ad units render test ads (with the Test Ad label) on that device, so no real impressions or clicks are recorded even though the build carries production IDs.
- Tell your testers plainly: don't tap ads unless they see the Test Ad label.
The takeaway
Treat every pre-production tap as real. Demo units for day-to-day development, registered test devices for release builds you test through Play, and real ad unit IDs only in the version that reaches actual users.
Ad-network policies change; before a launch, confirm the current test-ad guidance and demo unit IDs on Google's official AdMob documentation.