Stress test pour développeurs
Première fois ? Ce guide t’emmène du vocabulaire de base jusqu’à la lecture d’un rapport de charge — pour que tu saches exactement ce que tu mesures, et ce que tu ne mesures pas. L’exemple fil rouge : une boutique e-commerce fictive, ShopNova, pendant une vente flash.
1. Pourquoi faire un stress test ?
En développement, tu testes souvent avec 1 utilisateur : toi. Le jour d’une vente flash, d’une ouverture de billetterie ou d’un lancement produit, ce n’est plus 1 personne — c’est des milliers qui frappent les mêmes endpoints en même temps.
Un stress test répond à une question simple :
Ce que ça teste
Temps de réponse, erreurs, timeouts, saturation CPU / DB / CDN / serverless / files d’attente.
Ce que ça ne teste pas
Le design UI, les bugs métier rares, ni les services tiers hors périmètre (PSP, CDN média, e-mail…).
2. Vocabulaire indispensable
| Mot | Signification | Exemple |
|---|---|---|
| VU (Virtual User) | Un utilisateur simulé qui suit un scénario (comme un vrai navigateur). | --vus 500 = 500 VUs en parallèle |
| RPS | Requests Per Second — combien de requêtes HTTP / seconde. | ~800 RPS pendant un pic checkout |
| Latence | Temps entre l’envoi de la requête et la réponse complète. | p50 = 180 ms, p95 = 2,4 s… |
| Timeout | La réponse n’est pas arrivée avant la limite (souvent 10–30 s). | Compté comme fail capacité |
| Error rate | % de requêtes en échec (timeout, 5xx, réseau…). | Objectif SLA courant : < 1 % |
| Think-time | Pause entre deux actions (l’utilisateur “lit” la page). | 1,5 – 4,5 s entre catalogue et fiche produit |
| Spawn rate | Combien de VUs on démarre par seconde (la montée en charge). | 100/s → 5 000 VUs en ~50 s |
| Concurrency | Nombre max de requêtes HTTP en vol en même temps. | Plafond client ≠ nombre de VUs |
3. Modèles de charge (closed vs open)
Closed loop
Tu fixes un nombre de VUs. Chaque VU enchaîne : action → pause → action. C’est le modèle Locust / k6 closed. Il représente bien “N personnes connectées en même temps sur le site”.
Open loop
Tu fixes un débit (ex. 1 000 RPS), peu importe le nombre d’utilisateurs. Utile pour trouver la limite brute d’une API.
4. SLA et percentiles — comment décider PASS / FAIL
Une moyenne ment. Si 95 personnes ont 200 ms et 5 personnes ont 20 s, la moyenne peut “avoir l’air OK” alors que l’expérience est pourrie.
Percentiles
- p50 (médiane) : la moitié des requêtes est plus rapide.
- p95 : 95 % sont plus rapides — seul 5 % est pire.
- p99 : la queue extrême (souvent le début de la douleur).
SLA d’exemple (vente flash ShopNova)
1) taux d’erreur capacité < 1 %
2) p95 ≤ 2 secondes sur tous les endpoints critiques
(catalogue, fiche, panier, checkout)
5. Le parcours à stresser (exemple ShopNova)
Un bon scénario suit un parcours réel, pas “ouvrir la homepage en boucle”. Pour une vente flash e-commerce :
GET /products + GET /api/products?sale=flash
GET /products/{slug} + GET /api/products/{id}
POST /api/cart/items
GET /checkout + POST /api/orders
POST /api/payments/intent
(souvent mocké en preprod — ne DDoS pas ton PSP)
GET /orders/{id}
6. Outils courants
| Outil | Rôle |
|---|---|
| k6 | Script JS, métriques riches, CI-friendly — très utilisé en entreprise |
| Locust | Scénarios Python, UI web, bon pour itérer vite |
| Artillery / Gatling | Alternatives YAML / Scala selon les stacks d’équipe |
| Script custom (aiohttp, vegeta…) | Utile quand tu contrôles tout le parcours et le format de rapport |
Scénarios typiques
browse: catalogue + fiche (lecture seule)cart: browse + ajout paniercheckout: parcours complet jusqu’à la commande
7. Comment lancer (du plus sûr au plus dur)
# Exemple k6 — smoke k6 run --vus 5 --duration 30s checkout.js # Petit run réaliste k6 run --vus 200 --duration 2m checkout.js # Autour d’un plafond suspecté k6 run --vus 500 --duration 3m --ramp-up 30s checkout.js # Locust équivalent locust -f checkout.py --users 200 --spawn-rate 20 --run-time 2m --headless
8. Comment lire un rapport
Que tu sois en k6, Locust ou script custom, cherche toujours les mêmes blocs :
A. En-tête du run
{
"target": "https://preprod.shopnova.example",
"users": 500,
"vus_started": 500,
"scenario": "checkout",
"elapsed_s": 120
}
users= objectifvus_started= réellement démarrés (si < users, la montée n’a pas fini)scenario= quel parcours a été joué
B. Summary (le verdict rapide)
"summary": {
"requests": 18420,
"ok": 18310,
"fail": 110,
"error_rate_pct": 0.6,
"rps": 153.5,
"errors": { "timeout": 82, "http_503": 28 }
}
| Champ | Comment le lire |
|---|---|
ok | Réponses “saines” (2xx/3xx ou codes métier attendus) |
fail | Vraies pannes capacité |
error_rate_pct | fail / total — doit rester < 1 |
errors | ex. {"timeout": 82} → le serveur n’arrive plus à temps |
C. Endpoints (où ça fait mal)
{
"name": "POST /api/orders",
"requests": 2000,
"fail": 140,
"error_rate_pct": 7.0,
"latency_ms": { "p50": 900, "p95": 12000, "p99": 15000 }
}
Lis toujours par endpoint. Dans un run 1 000 VUs,
le catalogue peut rester à 0 % d’erreur pendant que
POST /api/orders timeout à 7 %. Le goulot = écriture commande / DB,
pas le listing HTML.
D. SLA + verdict
"sla": {
"pass_error_rate_lt_1pct": false,
"pass_p95_lt_2000ms": false
},
"verdict": {
"decision": "NO",
"supports_10k_checkout": false,
"headline": "Checkout p95 > 2s dès 800 VUs"
}
9. Visualiser et comparer
L’objectif n’est pas un joli graphe : c’est de trouver à partir de combien l’expérience utilisateur bascule.
10. Résultats d’exemple (ShopNova, preprod)
Parcours checkout authentifié, SLA strict (erreur < 1 % et p95 ≤ 2 s) :
| VUs | Err % | Checkout p95 | Lecture |
|---|---|---|---|
| 100 – 400 | 0 % | ≤ 800 ms | PASS — zone confortable |
| 500 – 700 | < 1 % | 2,5 – 6 s | Encore “up”, mais UX dégradée |
| 1 000 | 2,1 % | 12 s | FAIL — commandes en timeout |
| 10 000 | 28 % | 30 s | Cassé (pool DB + files) |
400 VUs — SLA OK
~600 VUs — lent
≥ 1000 VUs — cassé
POST /api/orders (écriture Postgres) est le premier mur.
11. Trouver le goulot (méthode)
- Regarde quel endpoint a le pire p95 / le plus de timeouts.
- Si c’est du HTML → edge / SSR / cold start / poids de page.
- Si c’est une API JSON → DB, cache Redis, auth, logique métier, locks stock.
- Si tu as des 429 → rate-limit (parfois voulu).
- Si tu as des 5xx → exception serveur / pool DB épuisé / worker saturé.
- Si tu as des erreurs côté client de test → bug du script, pas du serveur.
Sur ShopNova, le mur observé est l’écriture commande. Le catalogue HTML et l’API produits tiennent mieux grâce au cache. Le prochain mur, si tu ajoutes un suivi de panier temps réel, sera souvent Redis / WebSocket / Postgres sous écriture soutenue.
12. Pièges classiques (avancé)
1 compte ≠ N users
Un cookie / token partagé entre tous les VUs ne simule pas N sessions distinctes ni N paniers isolés.
Client vs serveur
Des erreurs dans le rapport peuvent venir du script (session fermée trop tôt, fuite de sockets) — pas de ton API.
Ne pas DDoS les tiers
Mocke le PSP, le SMS, l’e-mail, les webhooks externes. Tu testes ton app, pas Stripe.
Limites OS du runner
Fichiers ouverts, ports éphémères, CPU du laptop : le “plafond” peut être ton poste de test.
Environnement ≠ prod
Une preprod sous-dimensionnée donne un plafond pessimiste ; une preprod surdimensionnée ment dans l’autre sens.
p95 global
Si un seul endpoint critique dépasse 2 s, le SLA global FAIL — même si le reste est parfait. C’est voulu.
13. Pour monter au-delà du plafond (pistes)
- Cache / CDN agressif sur catalogue et fiches produit
- Pages statiques / ISR pour les contenus peu dynamiques
- Réduire le poids HTML/JS/images
- Pool DB + PgBouncer / ProxySQL, index, éviter les locks stock naïfs
- File d’attente pour les commandes (accepter puis confirmer)
- Tester avec N vrais comptes / paniers distincts avant le jour J
- Valider les quotas des services tiers (PSP, e-mail, SMS)
14. Aide-mémoire (imprime cette section)
QUESTION → MÉTRIQUE → BON SIGNE ------------------------------------------------------ Ça répond ? → error_rate_pct → < 1 % C’est rapide ? → latency p95 → ≤ 2000 ms Qui souffre ? → endpoint le + lent → isoler HTML vs API Assez de VUs ? → vus_started / users → ≈ 100 % Auth OK ? → login / session 200 Métier OK ? → codes attendus (pas 5xx) Tiers ? → hors scope (mockés) COMMANDES UTILES k6 run --vus 5 --duration 30s checkout.js k6 run --vus 500 --duration 3m checkout.js comparer les rapports 100 → 250 → 500 → 1000 PLAFOND EXEMPLE (ShopNova, checkout, SLA strict) ~400 acheteurs simultanés