Meilleures pratiques sur la stratégie de test pour une équipe Scrum



Un processus Scrum dépourvu d’un plan de test adéquat, c’est comme un POC (Proof of Concept) sous stéroïdes.

De nos jours, la majorité des projets réussis débutent par une phase de validation de concept, ou POC. Il s’agit d’une évaluation, généralement de petite envergure, d’une idée. Dans cette phase, la technologie sélectionnée et les fonctionnalités prévues sont examinées afin de déterminer leur impact potentiel sur les utilisateurs. Si la preuve est faite que le projet est viable, une équipe complète est alors constituée pour concevoir, développer et déployer le produit fini.

C’est un contexte idéal pour une équipe Scrum. Elle peut rapidement élaborer un prototype, en ajoutant des fonctionnalités significatives à chaque sprint. Les utilisateurs ont ainsi une visibilité en temps réel sur les progrès et sur la manière dont le projet se construit en l’espace de quelques sprints. Cela génère un impact positif, ce qui est l’objectif principal d’un POC. Cependant, cette phase comporte une particularité : l’absence ou la présence minimale d’activités de test. Même l’idée d’un processus de test serait perçue comme inappropriée.

Cette situation n’est pas désagréable au sein d’une équipe Scrum. La perspective de développer sans processus qui les ralentirait est généralement appréciée. C’est l’effet que toute activité de test peut avoir, ralentir le rythme. Et qui souhaite une lenteur lorsque l’objectif est de faire une forte impression sur l’utilisateur final ?

Si l’équipe continue de travailler de la même manière une fois la phase POC terminée, on se retrouve face à ce que j’appelle un POC sous stéroïdes. C’est un système de production dont la taille et les fonctionnalités ne cessent de croître, mais qui conserve les caractéristiques d’un POC : un produit complet en devenir, mais avec des failles cachées et des cas limites non explorés, une catastrophe potentielle en perspective.

Avant d’en arriver là, l’équipe aura de plus en plus de difficultés à maintenir des versions stables. La résolution des problèmes deviendra progressivement plus complexe.

Voici quelques méthodes qui se sont avérées efficaces lorsque j’ai été confronté à des problématiques similaires. Je les considère comme des bonnes pratiques pour mettre en œuvre des processus de test robustes, sans pour autant freiner la progression du développement. Après tout, c’est l’objectif d’une équipe Scrum : progresser rapidement et efficacement.

Répartir l’effort de test

Par où commencer, lorsqu’il s’agit de rendre l’effort moins pesant ? En le divisant, bien sûr !

Concrètement, il s’agit de créer un plan qui implique une petite charge de travail supplémentaire pour les développeurs. Cette charge additionnelle, bien que faible, aura un impact significatif sur l’objectif commun à long terme, de façon progressive et constante.

#1. Développer des tests unitaires pour chaque portion de nouveau code

Si vous parvenez à convaincre vos équipes Scrum d’intégrer le développement de tests unitaires pour chaque nouveau code créé pendant le sprint dans leur définition de « Terminé », vous aurez accompli un progrès considérable sur le long terme.

Les raisons sont assez évidentes :

Cela oblige les développeurs à envisager différentes voies non standard lors de la conception du code.

  • Ces tests unitaires peuvent être associés à des pipelines DevOps automatisés et exécutés à chaque déploiement dans l’environnement de développement ou de test. Les données du pipeline peuvent être facilement extraites puis utilisées pour illustrer auprès des utilisateurs professionnels le taux de couverture des cas de test liés directement au code source.

L’important est de commencer dès le début du projet. Plus tard on commencera à développer des tests unitaires, plus cela deviendra difficile pour les développeurs de les intégrer dans le sprint.

  • Il faudra un effort considérable pour développer des tests unitaires rétroactivement pour du code existant. Certaines parties du code ont pu être créées par un autre développeur, et la connaissance du fonctionnement exact de chaque portion de code n’est pas toujours claire pour le développeur actuel. Dans certains cas, la situation peut devenir telle que l’ajout d’un test unitaire au code modifié prendra plus de temps que le développement du changement de fonctionnalité pour le sprint (une situation que personne n’a certainement anticipée lors de la planification du sprint).

#2. Mettre en place une routine d’exécution des tests unitaires dans l’environnement de développement

Avant même de créer une requête d’extraction pour intégrer le nouveau code dans la branche principale, il devrait être une pratique courante de tester à la fois le code de fonctionnalité et le code de test unitaire dans l’environnement de développement. Cette procédure permettra de garantir que :

  • Le code de test unitaire fonctionne réellement pour chaque portion (car, après tout, il s’agit d’un code qui doit également être vérifié). Cette étape est souvent complètement ignorée. On suppose que si le test unitaire est branché sur le pipeline DevOps, il finira par être exécuté et testé quelque part par défaut. Cependant, cela ne fait que reporter les problèmes aux étapes ultérieures du sprint, où l’équipe dispose généralement de moins de temps et subit plus de pression pour terminer chaque tâche prévue.
  • Le nouveau code de fonctionnalité est testé par le développeur pour ses fonctionnalités de base. Bien sûr, il ne faut pas s’attendre à ce que ce test vérifie entièrement la fonctionnalité métier, mais au moins il confirmera que le code se comporte comme prévu par le développeur (sans tenir compte, pour le moment, du fait que la logique métier pourrait avoir été mal comprise pendant le développement).

#3. Prévoir l’exécution des tests unitaires après la fusion du code dans la branche principale

Avoir un code fonctionnel dans la branche locale (où seul le développeur concerné travaille sur de nouvelles fonctionnalités) est une chose. Avoir le même code fonctionnel après la demande d’intégration et la fusion dans la branche principale en est une autre.

La branche principale contient les modifications apportées par les autres membres de l’équipe Scrum. Même si les conflits ont été résolus et que tout semble correct, le code, après la fusion et la résolution des conflits, est fondamentalement un code non testé. Il est donc risqué de le transférer sans avoir vérifié qu’il fonctionne correctement.

Par conséquent, il est efficace de réexécuter les mêmes tests unitaires (déjà réalisés dans l’environnement de développement) dans l’environnement créé à partir de la version de code principale.

Pour les développeurs, cette étape supplémentaire peut sembler fastidieuse. Cependant, elle ne nécessite pas un gros effort puisque, dans ce cas, rien de nouveau ne doit être inventé : il suffit de répéter ce qui a déjà été fait une fois.

Cette étape peut toutefois être omise dans un cas précis :

  • Les tests unitaires automatisés dans les pipelines DevOps sont si complets qu’ils couvrent déjà l’ensemble des tests manuels exécutés en plus.

Bien qu’il soit tout à fait possible d’atteindre cet état, je n’ai jamais été témoin d’une telle situation en pratique. De plus, il faudrait probablement trop de temps aux développeurs pour créer des tests unitaires automatisés aussi détaillés. Il pourrait devenir inacceptable pour le propriétaire du produit de laisser l’équipe consacrer autant de temps à cette activité, car cela aurait un impact direct sur le nombre de tâches livrées dans un sprint.

Cependant, la priorité donnée au contenu du sprint ne doit jamais être une excuse pour ne pas effectuer les tests unitaires, voire les minimiser. En agissant ainsi, l’équipe se retrouvera dans un état où le code manquera de couverture de test unitaire. Et pour y remédier, il pourrait être trop tard (encore une fois, l’effort pour réaliser les tests unitaires serait supérieur à celui du changement de code pour le sprint).

C’est pourquoi je recommande sans hésiter la réexécution des tests unitaires sur la version de code principale. L’effort est minime par rapport à la valeur qu’il apporte.

Cette étape assure une dernière vérification de la préparation de la branche principale pour la phase de test de la version. Elle permet aussi de détecter la plupart des problèmes techniques (par exemple, les bugs qui empêchent l’exécution du code source jusqu’à la fin), ce qui permet à la phase suivante de se concentrer uniquement sur la vérification des fonctionnalités métier.

Se préparer aux tests fonctionnels

Toutes les étapes de test précédentes doivent aboutir à une conclusion précise : le code de la branche principale est exempt de bugs techniques et exécutable sans problème pour les flux fonctionnels de bout en bout.

Cette phase peut facilement être gérée par une seule personne, et cette personne n’a même pas besoin d’avoir une formation technique.

En fait, il est préférable que ce test soit effectué par une personne extérieure au groupe de développement, mais qui a une connaissance fonctionnelle de ce à quoi les utilisateurs s’attendent du comportement du code. Deux activités principales sont à réaliser :

#1. Tests fonctionnels ciblés des nouvelles tâches du sprint

Idéalement, chaque sprint doit apporter une nouvelle fonctionnalité (un incrément d’objectif de sprint) qui n’existait pas auparavant. Il faut donc la vérifier. Il est important de s’assurer que le nouveau logiciel fonctionne de manière satisfaisante pour les utilisateurs, qui l’attendaient évidemment depuis au moins tout le dernier sprint.

C’est une expérience décevante lorsque la fonctionnalité (longuement) promise est enfin annoncée pour être publiée, pour découvrir quelques jours plus tard qu’elle ne fonctionne pas correctement.

Il est donc essentiel de tester minutieusement les nouvelles fonctionnalités du sprint. Pour garantir le succès de cette démarche, il est conseillé de rassembler au préalable les cas de test importants auprès des parties prenantes concernées (qu’il s’agisse du propriétaire du produit ou même des utilisateurs finaux) et de dresser une liste de tous ces cas de test nécessaires pour le contenu du sprint.

Cela peut sembler fastidieux au premier abord, mais selon mon expérience, cette tâche peut tout à fait être gérée par une seule personne. Étant donné que les sprints sont généralement courts (environ deux semaines), la quantité de nouveau contenu est rarement trop importante, d’autant plus que le sprint comprend également des activités supplémentaires telles que la correction de la dette technique, la documentation, les tâches de conception, etc.

Les cas de test sont ensuite exécutés afin de vérifier en priorité les fonctionnalités attendues. Si des problèmes sont détectés, seul le développeur concerné est sollicité pour résoudre le problème, dans un délai raisonnable.

De cette manière, les développeurs consacreront un minimum de temps aux tests fonctionnels et pourront se concentrer sur les activités qu’ils apprécient le plus.

#2. Exécution des cas de test de régression

L’autre partie des tests fonctionnels consiste à s’assurer que ce qui fonctionnait jusqu’à présent fonctionnera toujours après la prochaine version. C’est l’objectif des tests de régression.

Il est préférable que les cas de test pour les tests de régression soient régulièrement mis à jour et réexaminés avant chaque version. En fonction des particularités du projet, il est conseillé de les maintenir simples tout en couvrant la majorité des fonctionnalités de base et des flux de bout en bout importants qui traversent l’ensemble du système.

En général, chaque système possède des processus de ce type qui concernent plusieurs domaines différents. Ce sont les meilleurs candidats pour les tests de régression.

Dans certains cas, les tests de régression couvrent même implicitement les nouvelles fonctionnalités du sprint, par exemple, si la nouvelle tâche du sprint modifie une partie spécifique d’un flux existant.

Ainsi, dans la plupart des cas, il n’est pas très complexe de réaliser des tests de régression en même temps que les tests de fonctionnalité des nouvelles tâches du sprint, surtout si cela est fait régulièrement avant chaque mise en production et si la fréquence des mises en production n’est pas trop élevée.

Insister sur l’exécution des tests d’assurance qualité avant chaque mise en production

Le test d’assurance qualité (QA) est la dernière étape avant la mise en production. Il est souvent négligé, car considéré comme peu important, notamment si l’équipe Scrum est sous forte pression pour produire de nouvelles fonctionnalités.

Même les utilisateurs professionnels diront qu’ils sont intéressés par les nouvelles fonctionnalités et moins par le maintien des fonctionnalités existantes ou un faible taux de défauts. Cependant, à terme, c’est la raison principale pour laquelle l’équipe de développement devra ralentir et les utilisateurs professionnels ne recevront pas ce qu’ils ont demandé.

Un test d’assurance qualité doit être effectué par des personnes extérieures à l’équipe Scrum, idéalement par les utilisateurs métier eux-mêmes dans un environnement dédié, aussi proche que possible de la production future. Le propriétaire du produit peut également remplacer les utilisateurs finaux.

Dans tous les cas, il doit s’agir d’un test propre, fonctionnel du point de vue de l’utilisateur final, sans aucun lien avec l’équipe de développement Scrum. Il offrira une vue finale du produit et sera utilisé d’une manière que personne de l’équipe Scrum n’avait prévue (ce n’est pas un scénario idéal, mais cela peut arriver). Il est encore temps pour les corrections de dernière minute.

Par ailleurs, il pourrait devenir clair que les attentes n’étaient pas bien comprises par l’équipe Scrum. Dans ce cas, nous pouvons au moins nous mettre d’accord sur un plan de suivi, bien avant la mise en production. Encore une fois, ce n’est pas idéal, mais c’est mieux que d’admettre l’échec après une annonce de mise en production réussie.

Que faire ensuite ?

L’application de ces pratiques au travail quotidien Scrum peut considérablement améliorer la stabilité et la prévisibilité des publications de sprint, sans retarder les mises en production ni passer tout le sprint à préparer la prochaine version. Cela évite également de contraindre tous les membres de l’équipe Scrum à réaliser des tâches qu’ils n’aiment pas ou qu’ils ne savent pas faire efficacement pendant la majorité du sprint.

Mais il ne faut surtout pas s’arrêter là.

L’intégration de tests de performance est un sujet à aborder. Ils sont souvent ignorés ou jugés inutiles, relégués lors des premières tentatives « d’optimisation des activités Scrum ». Cependant, j’ai toujours douté qu’un système de production puisse évoluer dans le temps s’il n’est pas régulièrement contrôlé pour ses performances.

Pour passer à la vitesse supérieure, il est également important de mettre en place des tests automatisés.

J’ai déjà évoqué une catégorie de tests automatisés (les tests unitaires dans les pipelines). Cependant, il est possible de développer des tests de régression complets de bout en bout, entièrement automatisés et exécutés automatiquement après chaque déploiement dans l’environnement de test. Cela soulagerait encore plus l’équipe de développement Scrum. Mais cela nécessite une équipe dédiée pour développer et maintenir ces tests automatisés. C’est un travail constant, car chaque fois que le code de production change, certains tests automatisés existants peuvent devenir invalides et doivent être mis à jour pour continuer à fonctionner dans les pipelines. C’est un investissement que peu sont prêts à faire, mais les avantages pour l’équipe de développement Scrum seraient considérables.

Ces sujets dépassent le cadre de cet article et nécessitent d’établir un calendrier précis et un timing optimal pour chaque type de test mentionné, afin qu’ils puissent être réalisés dans la fenêtre d’un sprint. J’ai hâte d’approfondir ces points la prochaine fois !