| name | load-testing-backend-from-mobile-perspective |
| description | Expert guidance on stress-testing backends from a mobile client's point of view — throttled networks, realistic request patterns, and client-side behavior under load. Use when asked how the app behaves on bad networks or under backend slowdowns. |
Load Testing from the Mobile Perspective
Instructions
Pure backend load tests (k6, Gatling, JMeter) measure the server's throughput and latency. They do not tell you whether the mobile app degrades gracefully when the server gets slow, when the network drops, or when 50 % of requests return 503. This skill is about testing the client's behavior under realistic adverse conditions.
1. What the Mobile Side Must Prove
Under stressed conditions the app should:
- Not crash. Timeouts, parse errors, and cancelled coroutines must be handled.
- Not spin. Requests must have bounded retries with exponential backoff + jitter.
- Not lose user input. Drafts survive a failed sync.
- Give honest feedback. Loading, error, and retry states are distinguishable to the user.
- Respect battery. No infinite retry loops; no polling faster than necessary.
2. Network Profiles to Cover
Use at least these profiles (copy-paste into your test harness):
| Profile | Down | Up | RTT | Loss |
|---|
| Wi-Fi | 50 Mbps | 20 Mbps | 20 ms | 0 % |
| 4G good | 12 Mbps | 5 Mbps | 60 ms | 0 % |
| 4G poor | 1 Mbps | 256 kbps | 300 ms | 2 % |
| 3G | 384 kbps | 128 kbps | 600 ms | 5 % |
| Edge | 100 kbps | 50 kbps | 900 ms | 10 % |
| Offline → online flip | — | — | — | 100 % → 0 % |
3. Android — Throttling Options
- Network Profiles (Android Studio): built-in emulator throttling.
- Charles Proxy / mitmproxy with throttling rules for device or emulator.
adb shell tc qdisc add dev eth0 root netem delay 300ms loss 2% on rooted emulators.
- In-app fault injection: ship a dev-only OkHttp
Interceptor that applies a sleep and configurable failure rate, gated by a feature flag.
4. iOS — Throttling Options
- Network Link Conditioner (macOS
Additional Tools for Xcode or installed on-device via the Developer menu).
- Proxyman / Charles with a device-level profile.
URLProtocol subclass in tests that injects configurable latency and failure rate.
5. Flutter / React Native
- Use the OS-level tools above; RN and Flutter share the host network stack.
- Add a Dio / axios interceptor for in-app fault injection during internal builds.
6. Server-Side Fault Injection in Tests
Run client code against a local mock server (MockWebServer, msw, shelf) that is scripted to misbehave:
server.enqueue(MockResponse().setSocketPolicy(SocketPolicy.NO_RESPONSE))
server.enqueue(MockResponse().setResponseCode(503).setBody("overloaded"))
server.enqueue(MockResponse().setResponseCode(200).setBody(validJson).throttleBody(1024, 1, TimeUnit.SECONDS))
Cover at least: hang + timeout, 500 / 503, 429 with Retry-After, partial body, invalid JSON, TLS failure.
7. Realistic Request Patterns
Real apps do not fire one request. A realistic client load test issues:
- Auth refresh + feed fetch + images + analytics in a single "screen open".
- N parallel image downloads.
- Background sync collision with foreground navigation.
Use a scripted flow (Maestro or a custom harness) that replays these patterns while throttling is on.
8. Assertions Worth Making
- Time-to-first-meaningful-content ≤ X seconds on each profile.
- Requests complete or fail with a user-visible error within Y seconds.
- Retry count per endpoint ≤ Z; total bytes sent on retry do not explode.
- No ANR (Android) or watchdog kills (iOS) during a 60-second degraded session.
- Persisted state (draft message, shopping cart) is intact after the network drops.
9. Driving Real Backend Load
When you do need a real-backend test from the mobile perspective:
- Use k6 or Gatling to generate server load while a real app session runs through Maestro.
- Measure the app's experience (screenshots, logs, timing) at various load percentiles.
- The aim is the client's response to slow server, not the server's throughput number.
10. Battery and Background
- Scripted 30-minute sessions with and without background, measuring battery drain per platform API (
BatteryManager, UIDevice.batteryLevel, MetricKit, Flutter battery_plus).
- Confirm that background sync, push, and prefetch back off when the OS signals low battery / Doze / Low Power Mode.
11. Checklist