| name | performance-testing |
| description | Tests de performance, charge et stress — k6, Locust, JMeter, ab, Artillery, métriques, analyse des bottlenecks, et CI/CD. |
| version | 1.0.0 |
| author | EVA |
| license | MIT |
| metadata | {"hermes":{"tags":["performance-testing","load-testing","stress-testing","k6","locust","jmeter","benchmarking"],"related_skills":["integration-testing","e2e-testing","security-testing"]}} |
Tests de Performance — Charge, Stress et Benchmark
Overview
Les tests de performance mesurent comment un système se comporte sous différentes conditions de charge. Ils détectent les régressions de performance, dimensionnent l'infrastructure, valident les SLAs, et préviennent les outages.
Types de Tests de Performance
| Type | Objectif | Charge | Durée |
|---|
| Load Test | Comportement sous charge normale attendue | 50-80% du pic | 15-30 min |
| Stress Test | Point de rupture | 150-200%+ du pic | Jusqu'à rupture |
| Spike Test | Réaction à une montée brutale | ×10 en 1-2s | Quelques minutes |
| Soak Test | Fuites mémoire / dégradation lente | Charge normale | 2-24h |
| Endurance Test | Stabilité long terme | 50-70% | 24-72h |
Indicateurs Clés
| Métrique | Seuil typique | Outil |
|---|
| P95/P99 Latency | < 500ms / < 2s | k6, JMeter |
| TPS (Throughput) | Objectif métier | Tous |
| Error Rate | < 0.1% | Tous |
| CPU / Memory | < 80% | htop, Prometheus |
| GC Pause (JVM) | < 200ms P99 | JFR, GC logs |
| DB Connection Pool | < 80% utilisé | pg_stat_activity |
| Disk I/O | < 80% iowait | iostat |
k6 — Outil Moderne (JavaScript/Go)
Installation et Configuration
sudo apt install k6
docker pull grafana/k6
brew install k6
Script de Load Test
import http from 'k6/http';
import { sleep, check, group } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';
const errorRate = new Rate('errors');
const loginDuration = new Trend('login_duration');
const loginFailures = new Counter('login_failures');
export const options = {
stages: [
{ duration: '1m', target: 10 },
{ duration: '3m', target: 50 },
{ duration: '1m', target: 100 },
{ duration: '2m', target: 100 },
{ duration: '1m', target: },
],
: {
: [, ],
: [],
: [],
: [],
},
: ,
};
= __ENV. || ;
() {
(, {
payload = .({
: ,
: ,
});
start = .();
res = http.(, payload, {
: { : },
: { : },
});
loginDuration.(.() - start);
errorRate.(res. !== );
(res, {
: r. === ,
: r.. < ,
: .(r.). !== ,
});
(res. !== ) {
loginFailures.();
}
();
});
(, {
res = http.(, {
: { : },
: { : },
});
(res, {
: r. === ,
: r.. < ,
});
();
});
}
Stress Test (point de rupture)
export const options = {
scenarios: {
stress: {
executor: 'ramping-arrival-rate',
startRate: 10,
timeUnit: '1s',
preAllocatedVUs: 100,
maxVUs: 500,
stages: [
{ duration: '5m', target: 50 },
{ duration: '5m', target: 100 },
{ duration: '5m', target: 200 },
{ duration: '5m', target: 400 },
{ duration: '5m', target: 600 },
],
},
},
thresholds: {
http_req_failed: ['rate<0.05'],
},
};
Soak Test (24h)
export const options = {
stages: [
{ duration: '5m', target: 50 },
{ duration: '24h', target: 50 },
{ duration: '5m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<1000'],
http_req_failed: ['rate<0.01'],
},
};
Test de l'API en Mode Brouillon
export const options = {
scenarios: {
spike: {
executor: 'constant-arrival-rate',
rate: 10,
duration: '30s',
preAllocatedVUs: 20,
maxVUs: 200,
},
},
};
Tests avec k6 Browser (E2E Performance)
import { browser } from 'k6/browser';
export const options = {
scenarios: {
ui: {
executor: 'shared-iterations',
options: { browser: { type: 'chromium' } },
},
},
};
export default async function () {
const page = await browser.newPage();
try {
await page.goto('https://example.com/login', { waitUntil: 'networkidle' });
const lcp = await page.evaluate(() =>
performance.getEntriesByType('paint').pop()?.startTime
);
await page.fill('#email', 'user@test.com');
await page.fill('#password', 'test123');
await Promise.all([
page.waitForNavigation(),
page.click(),
]);
metrics = page.();
.();
} {
page.();
}
}
Locust — Outil Python
Installation
pip install locust
Script de Test
from locust import HttpUser, task, between, tag
import json
class WebsiteUser(HttpUser):
"""Simule un utilisateur du site web."""
wait_time = between(1, 5)
def on_start(self):
"""Connexion au début de la session."""
response = self.client.post('/api/login', json={
'email': f'user{self.environment.runner.user_count}@test.com',
'password': 'test123',
})
if response.status_code == 200:
self.token = response.json().get('token')
else:
self.token = None
@tag('login')
@task(3)
def view_dashboard(self):
if self.token:
self.client.get(
'/api/dashboard',
headers={'Authorization': f'Bearer {self.token}'},
)
@tag()
():
.client.get()
():
.client.post(, json={
: , : ,
}, catch_response=) response:
response.status_code != :
response.failure()
order_id = response.json().get()
.client.get(, name=)
Exécution
locust --host=http://localhost:3000 --web-port=8089
locust --host=http://localhost:3000 --headless \
-u 100 --run-time 10m --spawn-rate 10 \
--csv=reports/locust
locust --host=http://localhost:3000 --tags login search
JMeter — Outil Java
Installation
wget https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
tar xzf apache-jmeter-*.tgz
./apache-jmeter-5.6.3/bin/jmeter.sh
./apache-jmeter-5.6.3/bin/jmeter.sh -n -t test-plan.jmx -l results.jtl
Test Plan Élémentaire (CLI via Taurus)
execution:
- executor: jmeter
scenario:
script: test-plan.jmx
concurrency: 50
ramp-up: 60s
hold-for: 5m
iterations: 100
reporting:
- module: final-stats
- module: console
pip install bzt
bzt bzt.yml
Tests de Performance API (ab, wrk, hey)
Apache Bench
ab -n 1000 -c 10 http://localhost:3000/api/health
ab -n 5000 -c 50 -T 'application/json' \
-p payload.json \
-H 'Authorization: Bearer token123' \
http://localhost:3000/api/login
WRK
wrk -t12 -c400 -d30s http://localhost:3000/api/health
wrk -t4 -c100 -d60s -s tests/perf/wrk-script.lua http://localhost:3000
Hey
hey -n 10000 -c 50 http://localhost:3000/api/health
hey -n 5000 -c 20 -H 'Authorization: Bearer token' \
-m POST -d '{"email":"test@test.com","pass":"test"}' \
http://localhost:3000/api/login
Analyse des Résultats
Que chercher dans les métriques ?
P95 Latency : Responsive ? La majorité des utilisateurs perçoit-elle la lenteur ?
P99 Latency : Queue ? Y a-t-il des utilisateurs bloqués ?
Error Rate : Stable ? Le système échoue-t-il sous charge ?
Throughput : Capable ? Le TPS atteint-il l'objectif ?
Seuils Communs
| Application | P95 | P99 | Taux Erreur |
|---|
| API REST standard | < 500ms | < 2s | < 0.1% |
| Page web SSR | < 1s | < 3s | < 0.1% |
| Base de données | < 50ms | < 200ms | < 0.01% |
| Streaming vidéo | < 200ms start | < 1s | < 1% |
| Paiement | < 2s | < 5s | < 0.01% |
CI/CD — Performance Gates
name: Performance Tests
on:
push:
branches: [main]
schedule:
- cron: '0 6 * * *'
jobs:
performance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Start services
run: docker compose up -d
- name: Run k6 tests
run: |
k6 run tests/performance/load-test.js \
-e BASE_URL=http://localhost:3000 \
--out json=reports/k6-report.json
- name: Check thresholds
run: |
# Analyser si les seuils ont été respectés
jq '.metrics.http_req_duration"p(95)".passes' reports/k6-report.json
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: k6-report
Bonnes Pratiques
- Toujours tester sur un environnement de préprod — jamais en production sauf smoke test
- Isoler l'infra — un serveur de test dédié ou conteneur isolé
- Métriques système + applicatives — CPU/RAM/IO sans l'app ne suffit pas
- Répéter 3 fois — une mesure isolée n'est pas significative
- Warm-up — laisser 1-2 minutes de montée avant de mesurer
- Monitorer le backend — les métriques frontend sans observabilité backend sont aveugles
- Comparer avec un baseline — toujours garder un résultat de référence
Common Pitfalls
- Tester en local — résultats non-représentatifs (latence réseau, ressources partagées)
- Ignorer le warm-up — JIT, caches, connections pools non initialisés
- Un seul run — les résultats varient, faire 3 runs consécutifs
- Pas de metrics système — 99% des goulots sont CPU, RAM, IO, réseau
- Charge trop simple — le même GET sur le même endpoint = pas réaliste
- Pas d'arrêt progressif — couper 500 connections d'un coup peut cacher des fuites
- Tester sur le même serveur que l'application — les résultats sont faussés
Verification Checklist