Editing a note offline creates a second version that must be reconciled with changes from another device. The question is not simply whether the app “works offline.” Testing must show which actions are local, when they are sent, what happens after a lost acknowledgement and how the product resolves two valid edits to the same record.
Define the promise of each offline action
Some screens can show cached content while disabling writes. Others allow local edits and promise later synchronisation. A form may let users type offline but refuse final submission. Give each operation a visible state such as saved locally, waiting to send, confirmed by server or needs attention. A single green check for all four states misleads users and makes test results hard to interpret.
Write an offline contract for the exact feature. For a note editor, a testable contract could be: text saves to local storage immediately, queued revisions upload after reconnection, a server conflict opens a review screen, and signing out removes access to the previous account's local copy. The storage and sign-out behavior should match the product's actual privacy design.
Network event | Action under test | Invariant to verify |
Offline before action | Edit a cached note | Local draft is readable after restart |
Disconnect during send | Tap Save once | One logical edit survives retries |
Response lost | Server commits, reply drops | Reconciliation finds existing revision |
Two devices edit | Reconnect both clients | Conflict rule preserves or presents both versions |
Account switch | Sign out with queued work | Work cannot leak into the next account |
Control the failure point, not only the signal icon
Airplane mode is useful for a complete disconnection but does not recreate a server that committed a write and lost only its response. Use a controllable test endpoint or proxy to fail a request before receipt, after receipt but before commit, and after commit but before the client receives confirmation. Inspect both client and server records after each run.
Change between Wi-Fi and cellular during a long transfer; then repeat with a captive portal that appears connected but cannot reach the service. A slow connection with occasional timeouts tests different behavior from a clean offline state. Record latency and failure injection in the case, since “poor network” is not a reproducible setting.
When using an emulator's network controls, confirm the client really passed through that route. A development host, local proxy or separate analytics SDK may use a different path. Repeat essential cases on a real phone before declaring a reconnect issue resolved.
Make queued work durable and accountable
Create three edits while offline, close the app and force a cold launch. The queue should still contain the correct account, target record, operation type and revision identity. Order matters if the second edit assumes the first succeeded. A queue that replays only the latest payload may be valid for replaceable draft text but unsafe for distinct actions such as adding three comments.
Give each logical command a stable identifier across retries. Test a double tap, automatic retry and manual retry against the same command. For a successful server commit followed by a lost reply, the retry should return or reconcile the original result. A new identifier on every attempt defeats duplicate protection.
Set a bounded retry schedule and a way to surface permanent errors. A revoked permission, invalid record or oversized attachment will not improve by retrying forever. The app should distinguish a temporary connection failure from an action that requires user input, and preserve enough information for the user to correct it.
Test conflict rules using actual revisions
Start two devices at server revision 12. Change the title on device A and the body on device B while B is offline. If the app merges fields, confirm that it understands which fields changed; blindly replacing the whole record can erase A's title. If the app uses last-write-wins, test whether that loss is acceptable for the data in question. A shared shopping list and a financial approval cannot safely inherit the same rule by accident.
Write expected results for same-field and different-field edits separately. Include delete versus edit, rename versus attachment upload, and a conflict that arrives while the user is reading a resolution screen. Preserve both original revisions until the conflict is resolved if the product promises manual review.
After resolution, reconnect the second device again and confirm that all three surfaces agree: device A, device B and the server. An apparently successful message on one phone is not enough if the other still has an unsent local copy that will overwrite it later.
Protect account boundaries and local data
Queue work under account A, sign out and sign in as B. The app must not upload A's note as B, show A's cached private content to B or silently discard a draft that its own UI claimed was saved. Test the intended sign-out path and a process restart before reconnect. The expected handling of unsent data must be specified before the test can pass or fail.
Also test token expiry while the queue waits. Refresh credentials for the same account where supported; require reauthentication when needed; never attach another user's token to an old command. Diagnostic logs may include operation IDs and error categories, but should avoid note contents, raw tokens and unnecessary identifiers.
Measure recovery as a user task
Do not judge sync only by whether the queue becomes empty. Time how long a user waits before a confirmed result is visible, how many taps are needed to resolve a conflict and whether an interrupted upload reports its final status. For a test run, create ten queued notes with two deliberate conflicts, then record confirmed, unresolved and duplicated counts. Ten is a chosen workload, not a platform limit.
Include storage exhaustion while persisting a draft and an app update with a nonempty queue. Verify that the previous valid draft remains readable after a failed write. Once repaired, rerun the lost-response path, because migrations and retries often touch the same stored operation format.
Distinguish service outage from device disconnection
A phone can have full signal while the application server returns an error. Test a DNS failure, an unreachable host, an authentication error and a deliberate service-side maintenance response as separate cases. The UI should not tell a user to turn on Wi-Fi when the actual problem is a server rejection. Preserve the command where retry is safe, and present the reason when intervention is required.
When the connection returns, avoid a burst of hundreds of simultaneous retries that overloads a recovering service. In a controlled test, queue twenty independent edits, reconnect and observe concurrency, completion order and failure handling. Twenty is a workload chosen for the experiment. Verify that a single blocked command does not prevent unrelated work forever unless ordering is part of the data contract.
Include clock skew: change the test device's time while offline and make another edit from a device with the correct clock. If the conflict rule uses client timestamps, a bad clock can incorrectly win. Server revisions or explicit conflict resolution provide more reliable evidence than an untrusted wall-clock value.
Trace one queued edit end to end
Assign a test edit a stable operation ID and record the local queue entry, first request, server revision and final UI state. Disconnect after the server commits but before the reply reaches the device. On reconnect, the retry should resolve to the earlier revision; it should not create a second note or erase a later edit. Repeat the sequence after signing out to confirm that the old account owns the command.
This trace distinguishes a transient transport error from a permanent validation failure. If the server rejects the edit because the target was deleted, keep the user-facing draft available for an explicit decision. Record the state transition and resulting record count in the test report, not just the presence of a success message.
Common questions about offline testing
Does an empty queue mean the data is safe?
No. The client might have dropped commands or cleared them before the server committed. Compare record revisions and operation IDs on both sides.
Can a cached screen be labelled offline-ready?
Only with a clear description of available actions. Viewing old data, editing locally and submitting a remote transaction are three different capabilities.
What is the most revealing reconnect case?
A server commit with a lost response exposes duplicate retries. Pair it with an edit from another device to expose conflict handling.
What if the service is reachable but rejects one edit?
Keep the rejected operation visible with its record and reason. A permission or validation error should not be retried forever as though it were a temporary outage. Let the user correct the input or discard the queued action deliberately; then verify that unrelated edits continue to sync.