How to Test Offline Mode in Mobile Apps (Complete Guide)
Offline mode is not just "the app does not crash when there is no network." Real offline support means caching the right data, queueing user actions, syncing when connectivity returns, and surfacing c
Offline mode is not just "the app does not crash when there is no network." Real offline support means caching the right data, queueing user actions, syncing when connectivity returns, and surfacing clear state to the user through all of it. This guide covers the tests that matter.
What "offline mode" should actually mean
A well-designed offline experience includes:
- Cache — recently viewed content available without network
- Queue — user actions (edit, post, favorite) stored locally and replayed on reconnect
- Status — clear UI indication of connectivity state
- Conflict resolution — when local changes and server changes diverge, rules are clear
- Degrade gracefully — features that truly need network show a helpful error, not a blank screen
Apps that handle all five ship strong offline support. Apps that handle only the first ship the "cached stale data with confusing UI" problem.
What to test
Detection
- Airplane mode on → app detects within 2 seconds
- Wi-Fi connected but no internet → app detects (captive portal, network issue)
- Slow 2G → app detects via request timeout, not via connection status alone
- VPN failure → app surfaces clear error, not silent freeze
- Connection returns → app detects and transitions to online mode
UI indication
- Offline banner or status pill visible when offline
- Last-updated timestamp shown for cached data
- Pull-to-refresh disabled or shows "offline" message, not spinner forever
- Buttons that require network (post, upload, pay) are disabled or show offline state
- Network-independent features (settings, help, previously downloaded content) fully usable
Cache
- Recently viewed content available offline
- Images cached and displayed (not broken icons)
- User data (profile, preferences) cached
- Cache size enforced (LRU eviction, not unbounded growth)
- Cache invalidated on user logout
Queue
- Edit / compose / post actions queued when offline
- Queue persists across app restart
- Queue visible to user ("3 pending actions")
- Queue drained on reconnect with visible progress
- Failed queue items (server rejected) surfaced with retry/discard options
Sync and conflict
- Reconnect triggers sync without user intervention
- Local changes merged with server changes (last-write-wins, or conflict UI)
- Concurrent edits on multiple devices resolved per rules
- User receives notification of sync failures with explanation
- Sync respects cellular vs Wi-Fi preferences
Edge cases
- Offline during signup → clear error, do not dead-end user
- Offline during payment → no partial charge
- Offline during critical security operation (password change) → refuse gracefully
- Content deleted server-side while user offline → removed on next sync with notification
- User logs out while offline → queued actions cleared or preserved per policy
- App backgrounded while offline, foregrounded after connectivity → catches up
- Storage full → cache fails gracefully, does not crash
- Clock skew between device and server → sync still works within reasonable tolerance
Accessibility
- Offline status announced to screen reader
- Offline state distinguishable from other error states
- Instructions for fixing (check Wi-Fi) accessible
Manual testing
Use Airplane Mode to simulate offline. Additionally test:
- Airplane mode on, then partial enable: Wi-Fi on, no router (ping fails) — different from airplane mode
- Slow network: use Network Link Conditioner (iOS) or
adb shell tc qdisc(Android) to throttle to 2G speeds - Packet loss: simulate 30% drop — requests time out, some succeed
- Flapping: toggle on and off repeatedly — app should not thrash
For each, walk through every major flow: login, browse, edit, submit. Verify the five properties (cache, queue, status, sync, graceful degrade).
Automated testing
Android — network conditions
// Via UiAutomator
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.executeShellCommand("svc data disable")
device.executeShellCommand("svc wifi disable")
// ... test offline behavior ...
device.executeShellCommand("svc wifi enable")
iOS — Network Link Conditioner
Automate via CLI on macOS CI runners. Simulator respects the conditioner profile.
Playwright — network offline
context.set_offline(True)
page.goto("/posts") # Should use cache
context.set_offline(False)
# Assert sync happened
Chaos testing — random offline toggles
import random, time
async def chaos_network(ctx):
for _ in range(20):
offline = random.random() < 0.3
await ctx.set_offline(offline)
time.sleep(random.uniform(0.5, 3))
How SUSA handles offline
SUSA's network_tester runs five network conditions against your app: slow 2G, high latency, packet loss, offline, and recovery (offline then back online). Each condition is tested for a window of the exploration and the app's behavior observed: did it crash, did it freeze, did it surface a useful message, did queued actions replay correctly.
susatest-agent test myapp.apk --network offline --steps 50
susatest-agent test myapp.apk --network network_recovery --steps 100
The impatient persona abandons if offline messaging is slow. The adversarial persona toggles connectivity mid-flow to stress-test sync. Findings include every flow that failed under a network condition, with the exact step at which it failed.
Common production bugs
- Cache eviction too aggressive — cached data disappears before user views it. Fix: separate LRU cache by content importance; prioritize recently-viewed.
- Queue does not survive app kill — SQLite not committed, in-memory queue lost. Fix: persist queue on every change, not on app suspend.
- Sync race on reconnect — two sync jobs fire simultaneously, duplicate submissions. Fix: single sync coordinator with dedup.
- "No internet" shown when server is actually down — app blames user's network for server outage. Fix: distinguish 5xx responses from connectivity failures.
- Offline toggle incorrect — app thinks offline when server is just slow. Fix: use actual connectivity (ConnectivityManager) plus request success rate, not just network type.
Offline is hard. Getting it right takes deliberate design, not accidents. Test every release — the distributed nature of the problem means subtle regressions are easy to ship.
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free