Load Testing API for Mobile Application
Mobile app with 10,000 active users at peak — not the same as 10,000 simultaneous HTTP requests. But close. Screens load in parallel, background sync via WorkManager and BGTaskScheduler runs independent of user actions, push notification can simultaneously wake thousands of users into app. Without API load testing backend breaks at the worst moment.
Tool Selection
Three main options, each with niche:
| Tool | Script Language | Strong Side |
|---|---|---|
| k6 | JavaScript | Modern, CI-friendly, low entry threshold |
| JMeter | XML / GUI | Mature, rich plugins, visual test design |
| Gatling | Scala / DSL | Precise metrics, convenient HTML reports |
For most mobile APIs choose k6 — compact scenarios, native Grafana integration, runs in Docker without JVM.
Writing k6 Scenario for Mobile API
Typical mobile API has specifics: JWT authorization with short TTL, refresh tokens, gzip compression, sometimes GraphQL instead of REST. Scenario must account for this.
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// Test accounts from CSV — not single account for all VU
const users = new SharedArray('users', () => open('./test_users.csv').split('\n').map(line => {
const [email, password] = line.split(',');
return { email, password };
}));
export const options = {
stages: [
{ duration: '2m', target: 100 }, // ramp up
{ duration: '5m', target: 500 }, // plateau
{ duration: '2m', target: 1000 }, // peak
{ duration: '1m', target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% requests < 500ms
http_req_failed: ['rate<0.01'], // less than 1% errors
},
};
export default function () {
const user = users[__VU % users.length];
// Login + get token
const loginRes = http.post('https://api.example.com/v1/auth/login', JSON.stringify({
email: user.email,
password: user.password,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, {
'login 200': (r) => r.status === 200,
'has token': (r) => r.json('data.access_token') !== undefined,
});
const token = loginRes.json('data.access_token');
sleep(1); // simulate user behavior
// Load feed
const feedRes = http.get('https://api.example.com/v1/feed?page=1&limit=20', {
headers: { Authorization: `Bearer ${token}` },
});
check(feedRes, {
'feed 200': (r) => r.status === 200,
'feed has items': (r) => r.json('data.items').length > 0,
});
sleep(2);
}
SharedArray for users — critical. If all Virtual Users use one account, backend may cache session and results will be incorrect.
Load Profiles for Mobile Applications
Mobile traffic uneven. Three typical profiles:
Baseline — stable load 24/7. Check p95 < 300 ms at average traffic.
Peak — morning and evening spike. Step growth from 10% to 300% average in 5 minutes.
Stress test — intentionally exceed calculated maximum, find breaking point. Until error rate exceeds 5% or latency grows 10x.
Soak test — 70% from peak for 4–8 hours. Catches memory leaks on backend, connection pool overflow to DB, log rotation.
Analyzing Bottlenecks
k6 sends metrics to Grafana via InfluxDB or built-in Prometheus remote write:
k6 run --out influxdb=http://localhost:8086/k6 scenario.js
After run check:
-
http_req_durationby percentiles (p50, p90, p95, p99) -
http_req_blocked— queue time (high value = connection pool exhausted) -
http_req_connecting— TCP connection setup time (high = no keep-alive) -
data_received— data volume (unexpectedly large = no gzip or extra response fields)
Typical bottlenecks in mobile API:
- N+1 queries to DB when loading feed with nested objects
- Missing index on
user_id+created_atin posts table - Synchronous push notification sending in request body instead of background queue
Running in CI
- name: Run k6 load test
uses: grafana/[email protected]
with:
filename: tests/load/api_test.js
flags: --duration 5m --vus 100
env:
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}
On CI run lightweight profile (100 VU, 5 minutes) — for baseline performance regression. Full stress test — on schedule or before release.
Timeline
3–5 days — writing scenarios for main endpoints, setting up load profiles, first run, results analysis and report with recommendations. Cost is calculated individually after API documentation review.







