Verdict
Yes
Yes, test how your app handles a slow network: with a fake server that trickles or withholds each response, assert that the screen shows a loading state, ends in an error with Retry within the call timeout, and sends no duplicate request.
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilityeventually
- Reversibilitycostly
- Test costmoderate
Test how your app handles a slow network, with a fake server that makes responses slow in CI. Blast radius is users: someone on a weak signal sees a spinner that never stops. Change frequency is regularly, because a new screen or request arrives about once a month and can miss a timeout. Detectability is eventually: the team works on office Wi-Fi, and a spinner raises no crash report. Reversibility is costly, because the fix needs an app store release and users who do not update keep the bug. Test cost is moderate, about an hour per screen, so rule R11 gives Test.
| When | Decision | Why |
|---|---|---|
| A slow response makes the user tap Pay again, and the second tap can charge the card twice | Test mandatory: hold the payment response, tap Pay twice, and assert both requests carry one idempotency key | Blast radius rises to money: a double charge ends in a refund |
| The app is a website that you redeploy in minutes, and its slow requests only read data, such as search | Test minimally: one browser test that holds the search response past the timeout and asserts the Retry message | Reversibility falls to trivial: a redeploy reaches every user at once, and a read stores nothing |
| The slow screen plays video, and the failure is stalls that depend on real bandwidth and the server's bitrates | Test it differently: track buffering time per connection type and alert when it rises after a release | Test cost rises to heavy and Reversibility falls to trivial: stalls vary in every run, and a server bitrate change fixes them without a release |
| Every request goes through one shared networking layer that sets the timeout, the retry and the error screen | Test: the shared layer against a throttled fake server, not each screen | Change frequency falls to rarely, but Blast radius users and Reversibility costly keep the decision at Test |
| The build is a prototype that only you run on your own phone | Do not test slow responses: add the call timeout and Retry before the first user installs it | Blast radius falls to none: only you wait for the spinner |
What breaks if you don't test
On Android, OkHttp's defaults limit each read to 10 seconds and set no limit on the whole call. On a weak signal a response arrives a few bytes at a time, no timeout fires, and the screen spins until the user closes the app. On iOS, timeoutIntervalForRequest restarts whenever data arrives, and the resource timeout defaults to seven days. The first signal is a "stuck loading" review, weeks later.
What you lose if you over-test
Real throttling in CI, through a proxy or emulator settings, makes timings vary: a test that expects the error after 20 seconds fails on a busy runner, and the team learns to rerun it. The end-to-end suite at EDGE speed waits on every screen load and finds the same failures as one throttled fake response per screen.
How to test
- Limit the whole call:
callTimeouton OkHttp,timeoutIntervalForResourceon iOS. One test asserts that the shared client has it. - For each screen that loads data, one test with a fake server, such as MockWebServer with
throttleBody, that trickles the response past the limit and asserts the error with Retry. - For each button that creates something, one test that holds the response, sends the action twice, and asserts one request reached the server.
- Before a release, open new screens on a slow profile: the Android emulator console command
network speed edge, or the Network Link Conditioner from Apple's WWDC19 session.
When the answer changes
- A slow request can charge a card or place an order.
- The app is a website, so a fix reaches every user in minutes.
- The slow screen streams media whose quality depends on real bandwidth.
Real incident + Code example
The parcel history that loaded for three minutes
On a parcel-tracking app I worked on, the Android client used OkHttp's default timeouts. A busy parcel's history was 400 KB of JSON. At about 2 KB per second on a rural signal, the download took over three minutes, and each read got bytes inside the 10-second read timeout, so the spinner never stopped. Reviews said "stuck loading" for three weeks before we logged the connection type and saw every stalled load on 2G or weak 3G. The fix set a 20-second call timeout, added Retry and paged the history, and took eight days to reach most users. These tests now run in CI:
class SlowNetworkTest {
private val server = MockWebServer().apply { start() }
@AfterTest fun stop() = server.close()
@Test fun sharedClientLimitsTheWholeCall() {
assertEquals(20_000, Http.client.callTimeoutMillis)
}
@Test fun tricklingHistoryEndsInTimeout() {
server.enqueue(
MockResponse.Builder()
.body("x".repeat(10_000))
.throttleBody(1_000, 1, TimeUnit.SECONDS) // 1 KB per second
.build()
)
// shorter limit, so the test takes 2 seconds, not 20
val client = Http.client.newBuilder().callTimeout(2, TimeUnit.SECONDS).build()
val repo = TrackingRepository(server.url("/"), client)
assertEquals(HistoryResult.TimedOut, repo.history("PARCEL-1"))
}
}
Related questions
- Should I test on low-end devices?Yes
- Is testing on an emulator enough?Code under test: Yes
- Should I write performance tests?Test it differently
- Should I test error handling?Yes
- Should unit tests make real API calls?Test it differently
FAQ
- How do I simulate a slow network on the Android emulator?
Run the emulator console commands
network speed edgeandnetwork delay edge, for example throughadb emu. EDGE gives 236.8 kbps down and up to 400 ms of latency.- How do I simulate a slow network on the iOS simulator?
Install the Network Link Conditioner from Apple's Additional Tools for Xcode and pick a profile such as 3G; it slows the whole Mac, including the simulator. On a real iPhone the conditioner is in Settings under Developer.
- Should I test my app offline?
Yes, test offline in the same tests as a slow network: a fake server that refuses the connection must lead to an error with Retry. Offline is cheaper to test, because the error arrives at once.
- Can I test slow network conditions in CI?
Yes, test slow responses in CI with a fake server that throttles them, such as MockWebServer, and a short call timeout so the test runs in seconds. Keep real network throttling out of CI, because its timings vary between runs.