Les tests de montée en charge font partie des tests à réaliser pendant la recette d'un projet. Cela permet de vérifier la livraison en situation réelle.

Les projets d'API REST consistent à livrer une série d'URL internet n'ayant pas pour vocation d'être appelées directement par un utilisateur, mais exploitées par un logiciel / un site pour récupérer et envoyer des informations. Pour les tester, il ne faut pas simplement vérifier que chaque endpoint, point de terminaison : URL correspondant à une fonctionnalité précise, réponde correctement et dans un temps raisonnable. Il faut également s’assurer que l'ensemble du projet fonctionne correctement avec un grand nombre d'utilisateurs.

Ces tests s’appellent des tests de montée en charge et permettent de garantir la qualité du service rendu par les API.

Cet article vous présentera les différentes étapes de leur préparation et de leur mise en œuvre.

Identifier des profils utilisateurs

Lorsque que l'on parle de profil ici, il ne s'agit pas de qui sont les utilisateurs mais de ce qu'il font et de comment ils vont utiliser les APIs. Dans le cadre qui nous a intéressé, les utilisateurs proviennent d'une application mobile.

Nous avons identifié un certain nombre d'appels qui sont réalisés systématiquement lancement de l'application, puis nous avons également analysé quatre cas d'usages correspondant à quatre aspects phares de l'application.

Pour chacun de ces usages, nous avons déterminé quels sont les appels susceptibles d'être réalisés. Nous avons également estimé les statistiques d'usage de chacun de ces scénarios.

Utilisation du hasard

const getRandomCityName = () => {
    const randomIndex = Math.floor(Math.random() * config.cities.length);
    return config.cities[randomIndex];
  };

Afin de réaliser les tests les plus réalistes possible, nous nous fions au hasard. En effet, le choix d'appliquer l'un ou l'autre des scénarios utilisateur est réalisé suite à la génération d'une variable aléatoire et à l'attribution à chaque scénario de plages de valeur. Ainsi si nous avons deux scénarios, et que l'on considère qu'un utilisateur sur trois choisira le premier scénario et deux sur trois le second, nous générons une valeur aléatoire entre 0 et 3 (exclus). Si la valeur obtenue est inférieure strictement à 1 nous appliquons le premier écran, sinon nous appliquons le second scénario.

De même, les paramètres de type texte sont choisis aléatoirement parmi des listes pré-déterminées, et les paramètres numériques sont obtenus également à partir de valeurs pré-déterminées auxquels nous ajoutons des variations aléatoires.

Le hasard est très important dans le sens où il permet de tester de façon plus réaliste : en faisant varier les valeurs, et en choisissant les scénarios en se fiant à la fois aux statistiques et au hasard, nous évitons de tomber dans des tests trop 'gentils' qui bénéficient trop souvent de cache applicatifs (exemple pour les recherches géographiques utilisant les coordonnées GPS de l’utilisateur).

Implémentation des tests

Pour réaliser les tests de montée en charge, nous avons utilisé K6, un outil open source pour définir les tests en javascript.

Nous réalisons ainsi :

  • un fichier JS avec les fonctions utilisées souvent,

  • un fichier JS par scénario utilisateur,

  • un fichier JS pour réaliser la répartition entre les différents scénarios utilisateurs,

  • un fichier JS pour le test de montée en charge proprement dit

L'avantage de ce découpage est qu'il permet de tester les différents éléments du test indépendamment les uns des autres puis de tester l'assemblage.

Pour l'exploitation des résultats du test, la possibilité d'utiliser l'outil de visualisation Grafana est un avantage non négligeable dans le sens où la lecture est plus aisée sur des graphes que dans un fichier CSV. Nous pouvons ainsi plus facilement nous rendre compte du nombre d'utilisateurs simultané nécessaire pour 'mettre à genou' le serveur, et déterminer les endpoints les plus lent à répondre et donc devant faire l'objet d'un effort d'optimisation.

import user from './user.js';
import { sleep } from 'k6';
export let options = {
    stages: [
        { duration: '2m', target: 10 },
        { duration: '5m', target: 10 },
        { duration: '2m', target: 20 },
        { duration: '5m', target: 20 },
        { duration: '2m', target: 30 },
        { duration: '5m', target: 30 },
        { duration: '2m', target: 40 },
        { duration: '5m', target: 40 },
        { duration: '2m', target: 50 },
        { duration: '5m', target: 50 },
        { duration: '2m', target: 60 },
        { duration: '5m', target: 60 },
        { duration: '5m', target: 0 },
    ],
};
export default function () {
    user();
    sleep(1);
}

Conclusion

Les tests de montée en charge sont un élément important de la recette logicielle et ne doivent pas être négligés. Ils requièrent une bonne anticipation de l'usage qui sera fait du système d'information afin d'être le plus réaliste possible, et des outils de visualisation afin d'être capable d'exploiter facilement les données.

Ces tests permettent généralement d’identifier quelques points faibles et d’optimiser ainsi la qualité de service rendu.

Vous avez un projet ? Concrétisons vos idées.