Les tests de performance répondent à des questions que les tests fonctionnels ne posent pas. Ils mesurent le comportement sous charge soutenue (tests de charge), le point de rupture du système (tests de stress) et la réaction aux pics de trafic soudains.
Pourquoi les tests de performance sont nécessaires
Un formulaire de connexion qui répond en 200 ms, c'est bien. En 5 secondes, les utilisateurs partent. En 30 secondes sous charge, le serveur tombe.
Les bugs de performance sont souvent les plus difficiles à corriger : ils nécessitent des changements architecturaux, pas de simples corrections de code. Les détecter tôt fait la différence.
Pannes de performance courantes :- Des requêtes de base de données qui fonctionnent bien avec 100 lignes et qui dépassent le timeout avec 1 million
- Des endpoints API qui gèrent 10 utilisateurs simultanés et échouent à 100
- Des fuites mémoire qui s'accumulent sur des heures d'utilisation
- Des intégrations tierces qui deviennent des goulots d'étranglement sous charge
Types de tests de performance
Tests de charge
Simulent la charge de production attendue pour vérifier que le système fonctionne dans les limites acceptables.
Question : Le système gère-t-il le trafic normal ? Exemple : L'application compte 500 utilisateurs simultanés pendant les heures de bureau. Lancez 500 utilisateurs virtuels pendant 30 minutes et mesurez les temps de réponse. Résultats acceptables :- Temps de réponse au 95e percentile inférieur à 1 seconde
- Taux d'erreur inférieur à 1 %
- Pas de fuites mémoire
Tests de stress
Poussent le système au-delà de ses limites pour trouver le point de rupture.
Question : Comment le système se comporte-t-il en cas de surcharge ? Échoue-t-il gracieusement ? Exemple : Démarrez avec 100 utilisateurs, augmentez de 100 toutes les minutes jusqu'à la rupture. Observez quand les erreurs apparaissent, comment le système tombe, et s'il récupère.Tests de pic
Augmentation massive et soudaine de la charge, puis retour à la normale.
Question : Le système peut-il gérer des pics de trafic soudains ? Exemple : La charge normale est de 100 utilisateurs. Passez subitement à 1 000 pendant 2 minutes, puis revenez à 100. Cas typiques : Mentions sur les réseaux sociaux, articles de presse, ventes flash.Tests d'endurance
Charge soutenue sur une longue durée.
Question : Y a-t-il des fuites mémoire ou une dégradation des performances dans le temps ? Exemple : 200 utilisateurs simultanés pendant 8 heures. Surveillez l'utilisation mémoire et l'évolution des temps de réponse.k6 : l'outil de test de charge moderne
k6 est l'outil de test de charge le plus accessible pour les ingénieurs QA : basé sur JavaScript, s'exécute en ligne de commande, s'intègre dans les pipelines CI.
Installation
# macOS
brew install k6
# Windows (winget)
winget install k6
# Docker
docker pull grafana/k6Votre premier test de charge
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
// Configuration du test
export const options = {
vus: 50, // Utilisateurs virtuels
duration: '30s', // Durée d'exécution : 30 secondes
};
export default function() {
// Effectuer une requête
const response = http.get('https://lab.becomeqa.com/api/products');
// Assertions
check(response, {
'statut est 200': (r) => r.status === 200,
'temps de réponse < 500ms': (r) => r.timings.duration < 500,
'contient des produits': (r) => JSON.parse(r.body).length > 0,
});
// Attente entre les itérations (simule le comportement d'un vrai utilisateur)
sleep(1);
}Exécution :
k6 run load-test.jsRésultat :
✓ statut est 200 100.00% ✓ 1500 ✗ 0
✓ temps de réponse < 500ms 95.33% ✓ 1430 ✗ 70
✓ contient des produits 100.00% ✓ 1500 ✗ 0
http_req_duration..............: avg=185ms min=45ms med=160ms max=1.2s p(90)=350ms p(95)=480ms
http_reqs......................: 1500 49.91/sScénarios de montée en charge
Plus réaliste que d'atteindre immédiatement la charge maximale :
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Monter à 100 utilisateurs en 2 minutes
{ duration: '5m', target: 100 }, // Maintenir 100 utilisateurs pendant 5 minutes
{ duration: '2m', target: 200 }, // Monter à 200
{ duration: '5m', target: 200 }, // Maintenir 200 utilisateurs
{ duration: '2m', target: 0 }, // Redescendre
],
};Tester un endpoint API avec authentification
import http from 'k6/http';
import { check, group } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 50 },
{ duration: '3m', target: 50 },
{ duration: '1m', target: 0 },
],
thresholds: {
'http_req_duration': ['p(95)<500'], // 95% des requêtes en dessous de 500ms
'http_req_failed': ['rate<0.01'], // Moins de 1% d'erreurs
},
};
export function setup() {
// Connexion unique, token partagé entre tous les utilisateurs virtuels
const res = http.post('https://api.myapp.com/auth/login', JSON.stringify({
email: 'load-test@myapp.com',
password: 'LoadTestPass1',
}), { headers: { 'Content-Type': 'application/json' } });
return { token: JSON.parse(res.body).token };
}
export default function(data) {
const headers = {
'Authorization': `Bearer ${data.token}`,
'Content-Type': 'application/json',
};
group('Liste des produits', () => {
const res = http.get('https://api.myapp.com/products', { headers });
check(res, { 'produits chargés': (r) => r.status === 200 });
});
group('Détail d\'un produit', () => {
const res = http.get('https://api.myapp.com/products/1', { headers });
check(res, { 'détail chargé': (r) => r.status === 200 });
});
sleep(Math.random() * 3 + 1); // Temps de réflexion aléatoire : 1-4 secondes
}Seuils de performance
Définissez les critères de réussite et d'échec :
export const options = {
thresholds: {
// 95% des requêtes doivent se terminer en moins de 500ms
'http_req_duration': ['p(95)<500'],
// Seuil spécifique pour un endpoint
'http_req_duration{name:products}': ['p(95)<300'],
// Moins de 0,1% d'erreurs tolérées
'http_req_failed': ['rate<0.001'],
// Seuil pour une métrique personnalisée
'checkout_duration': ['p(90)<2000'],
},
};Si les seuils ne sont pas atteints, k6 se termine avec un code non nul : le pipeline CI échoue automatiquement.
Ce qu'il faut mesurer
Percentiles du temps de réponse :- p50 (médiane) : 50% des requêtes sont plus rapides que cette valeur
- p90 : 90% des requêtes sont plus rapides
- p95 : 95% des requêtes sont plus rapides
- p99 : la queue lente, ce que vivent les 1% les moins bien servis
Intégration dans le pipeline CI
# GitHub Actions
performance-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Installer k6
run: |
curl https://github.com/grafana/k6/releases/download/v0.50.0/k6-v0.50.0-linux-amd64.tar.gz -L | tar xvz
sudo mv k6-v0.50.0-linux-amd64/k6 /usr/local/bin
- name: Lancer le test de charge
run: k6 run load-tests/api-load-test.js
env:
BASE_URL: ${{ secrets.STAGING_URL }}
- name: Enregistrer les résultats
uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: k6-results.jsonProblèmes de performance courants à surveiller
Requêtes de base de données lentes : surveiller le temps d'exécution sous charge, les requêtes N+1 (une requête par ligne au lieu d'une seule), et les index manquants. Fuites mémoire : une utilisation mémoire qui croît dans le temps sans redescendre, visible surtout dans les tests d'endurance. Épuisement du pool de connexions : les connexions à la base de données s'épuisent sous haute concurrence, avec des erreurs "too many connections". Goulots d'étranglement tiers : une API externe qui ralentit sous charge, ou une passerelle de paiement avec des limites de débit. Cache manquant : les mêmes données récupérées répétitivement depuis la base de données alors qu'elles pourraient être mises en cache.Récapitulatif
| Type de test | Objectif | Durée |
|---|---|---|
| Test de charge | Vérifier les performances en charge normale | 30 min à 2 h |
| Test de stress | Trouver le point de rupture | Jusqu'à l'échec |
| Test de pic | Gérer les montées de trafic soudaines | Pics courts |
| Test d'endurance | Détecter les fuites mémoire | Des heures |
Bases de k6 :vus: utilisateurs virtuelsduration: durée d'exécutionstages: schémas de montée en chargethresholds: critères de réussite/écheccheck(): assertions par requête
Les tests de performance sont distincts des tests fonctionnels : outils différents, objectifs différents. Les tests fonctionnels vérifient la correction ; les tests de performance vérifient la rapidité et la fiabilité sous charge. Ne lancez jamais un test de stress sur un environnement de production partagé : un test de stress cherche par définition à faire tomber le système.
→ See also: Tests de Performance avec k6: Le Premier Test de Charge de l'Ingénieur QA | La Pyramide des Tests Expliquée pour les Ingénieurs QA | Tests d'API 101: Tout ce que Chaque Ingénieur QA Doit Savoir en 2026