Why Your Push Notifications Are Failing — And How QA Can Catch It Before Users Do
Your step-by-step survival playbook for testing, verifying, and saving your team from push notification disasters.
Let’s set the stage.
You come into work. CI is green. The automated suite passed. Regression is clean. You feel good. But then a Slack message hits:
“Push notifications aren’t showing.” “Users say they’re not getting alerts.” “Support is flooded.”
Your stomach sinks. Logs look fine. Local tests pass. Everything “should” be working—but it clearly isn’t.
If this sounds familiar, welcome. You’ve entered the frustrating world of push notification debugging, where everything is asynchronous, invisible, and often misunderstood. This article is for QA engineers, developers, and mobile teams who want to stop guessing and start diagnosing.
First Truth: Push ≠ Notification
Let’s start with the most common misconception.
A push is a backend message sent via a service like Firebase or APNS. A notification is what the user actually sees on screen.
They’re not the same. You can receive a push and not display a notification. You can also show a notification without ever using push (e.g., local reminders).
Understanding this distinction is critical. It shapes how you test, monitor, and debug.
Real Example:
Your app polls the server while in the foreground and shows a local notification. In this case, FCM isn’t involved at all. If you’re debugging token delivery or waiting for logs from Firebase—you’re wasting time. This isn’t a push issue.
How Push Notifications Actually Work
Here’s a simplified architecture of how most push flows are wired:
- App registers with a push provider (e.g., Firebase, APNS).
- The provider returns a device token.
- Your app sends this token to your backend.
- When an event occurs (e.g., new message), the backend sends a push to that token via FCM/APNS.
- The provider routes the message to the device.
-
The device either:
- Displays the notification (automatically or via app logic), or
- Receives it silently (data-only pushes).
Push Payload Types (in FCM)
Pushes are not one-size-fits-all. Knowing the type of message makes all the difference in testing.
1. Notification-Only
These show a banner or alert only when the app is backgrounded or killed. Foreground = silent failure unless you write code to handle it.
{
"message": {
"token": "ABC",
"notification": {
"title": "New Match!",
"body": "You’ve got a new connection!"
}
}
}
2. Data-Only
These never display anything. Your app receives the payload and acts on it (e.g., preloading data, syncing state).
{
"message": {
"token": "ABC",
"data": {
"type": "refresh",
"contentId": "123"
}
}
}
Even if notification permissions are denied, these will still come through.
3. Mixed (Notification + Data)
Includes both notification and data. Behavior depends on app state.
- Background: The system displays the notification;
data
is passed only if tapped. - Foreground: Entire payload is delivered to the app, but nothing is shown automatically.
Not All Notifications Come From Push
You’ll likely also encounter:
- Local notifications (timers, reminders).
- WorkManager/AlarmManager triggers (background tasks).
- Persistent service notifications (foreground services on Android).
Talk to your developers. Document all sources of notification logic. Don’t assume everything’s push.
Why Push Notifications Fail Silently
Let’s be blunt: push services don’t guarantee delivery.
Here’s what can go wrong:
Platform/Device Issues
- Device was offline, and TTL (time-to-live) expired.
- Notification permissions revoked.
- App was force-stopped.
- Battery optimizations killed background receivers.
- OEM-level restrictions (e.g., Xiaomi, Huawei, Oppo).
Backend or Logic Issues
- Invalid or expired token.
- Incorrect payload structure.
- Notification channel misconfiguration.
- Race conditions in rendering logic.
Service-Level Quirks
- Silent pushes misused → FCM may throttle your high-priority flags.
- Doze mode (Android) or Focus mode (iOS) delaying delivery.
- Ports (e.g., 5228–5230, 443) blocked by firewall or VPN.
If you don’t test for these, they’ll bite you in production.
When the Push Arrives… But Nothing Shows
Sometimes the payload is delivered—but nothing pops up on screen.
Here’s what to check:
- Was the app in foreground? If yes, system won’t show notification by default.
- Notification channel muted or missing? Android won’t display anything.
- Permissions revoked? App might silently receive pushes but can’t alert.
- Foreground handler logic buggy or missing? You need to code rendering manually.
🧠 dontkillmyapp.com is a great resource for known manufacturer-level restrictions.
Logging & Monitoring Push Behavior
To debug this properly, you need observability from both sides.
Use Firebase Delivery Data
Great for macro-level metrics:
- Delivery success/failure
- Throttling reports
- Device registration stats
But it won’t tell you if the app actually rendered the notification.
Add Custom In-App Logging
Have the app log:
- Token registration
- Push receipt
- Notification rendering
- Tap/open events
Send those logs to your backend or analytics platform. This gives you full visibility—especially useful for A/B tests and incident reviews.
QA Checklist: How to Stay Ahead of Push Problems
Here’s what I recommend every team implement:
✅ Test all app states (foreground, background, killed) ✅ Simulate offline delivery + TTL expiry ✅ Validate token lifecycle (registration, invalidation) ✅ Test on real devices with battery limits and VPNs ✅ Automate regression checks for all payload types ✅ Include notification channel validation in test cases ✅ Implement polling fallback when push fails ✅ Collect push → render → open metrics
Final Thoughts: Pushes Are Tricky, But Masterable
Push notification bugs are elusive. They cross boundaries—backend, mobile, OS, user settings. That’s why they often go unnoticed until it’s too late.
But that also means: if you understand how they work, you become the go-to expert.
Treat push delivery and notification rendering as separate systems. Build observability, cover edge cases, and document your infrastructure.
Do that, and you won’t just debug push issues—you’ll prevent them.
Follow Me
🐦 X / Twitter
🎨 Instagram
🐘 Mastodon
🧵 Threads
🎸 Facebook
🧊 Bluesky
💻 LinkedIn
🐈 GitHub