Offline mode testing is not one test — it is a family of scenarios that most test plans compress into "turn on airplane mode, check the error message." Real users ride elevators, sit on trains between cell towers, and connect to hotel wifi that technically works. The gap between fully online and fully offline is where mobile apps actually fail, and it deserves more than one line in the plan. Here is how I cover it.
Offline mode testing starts mid-flow
Toggling airplane mode while parked on a settings screen tells you almost nothing. Toggle it mid-flow instead: after tapping pay but before the confirmation, halfway through an upload, between typing a message and hitting send. The cruelest window is after the request has left the device but before the response arrives — did the payment go through? The app should be able to answer that question, or at least fail toward safety. On Android I script the toggle so it lands at a consistent moment:
adb shell cmd connectivity airplane-mode enable
adb shell cmd connectivity airplane-mode disable
Request queuing and duplicates
Agree with the team on which actions queue while offline and which fail immediately, then verify both lists against reality. For queued actions, check ordering: edits should replay in the sequence the user made them. For retries, hunt duplicates — tap send while offline, watch the spinner, reconnect, and count how many copies actually arrive. Idempotency bugs love this exact path, and they are much cheaper to find in QA than in a payments dispute.
Conflict resolution on reconnect
Edit the same record from two places: change it offline on the device while the server-side copy also changes, then reconnect. Whatever the product's stated policy is — last write wins, merge, or ask the user — verify it actually happens and that neither version silently disappears. Silent data loss is the worst outcome offline support can produce, strictly worse than an honest error message.
Cache staleness and user messaging
Cached data is a feature until it lies. Check what the app shows after a day offline: are prices, stock levels, or balances presented as fresh when they are anything but? A timestamp or a small offline banner is cheap honesty. While you are there, audit every failure message the offline paths produce — "Something went wrong" over an infinite spinner is a bug in its own right. Users forgive being offline; they do not forgive being lied to about it.
Throttling: the network between the networks
Full offline is the easy half. Use throttling profiles to simulate the ugly middle: Network Link Conditioner on iOS has presets like 3G and 100 percent packet loss, the Android emulator can constrain speed and latency, and a proxy such as Charles can degrade traffic on a real device. My priority profiles are high latency (exposes timeouts and loading states), lossy connection (exposes retry storms), and very low bandwidth (exposes progressive loading and image fallbacks). Poor network finds bugs that pure offline never will, because requests half-succeed.
Poor-network testing is miserable to do by hand, which is exactly why it gets skipped. I keep the airplane-mode toggle scripted around a handful of Maestro flows so the basic offline paths run on every regression cycle in CI, without anyone having to volunteer to suffer through them manually.
Offline support is a product promise, not just an engineering feature, and it should be tested like one: at the worst possible moments, on the worst plausible networks, with the user's data on the line. If your app claims to work offline, prove it in the elevator, not just in the demo.