Le TDD est-il un frein à l'agilité ?
L'agilité est sans doute une question de subtilité. Et il est parfois difficile de trouver le point d'équilibre entre adaptabilité et qualité. Ici, on montre que les méthodologies de test approfondies doivent être soigneusement appréhendées à l'aune des objectifs de productivité des équipes.
Le Test Driven Development (TDD) est une pratique de développement logiciel dans laquelle les tests automatisés sont écrits avant le code de production. Le but visé étant de garantir que le code corresponde strictement aux spécifications, en répondant aux exigences de tests circonstanciés, tout en facilitant la maintenance du code. Difficile de faire plus agile… Mais ça, c'est la théorie.
En effet, il est indéniable que le TDD puisse aider à maintenir l'agilité dans un projet. En améliorant la qualité du code et en fournissant une base solide pour la refactorisation, on s'assure d'une couverture fonctionnelle significative. Cependant, cela pose en général quelques défis, car les tests unitaires ne sont pas toujours suffisants pour garantir l'intégration du logiciel. En effet, les tests unitaires sont conçus pour vérifier le comportement des éléments individuels d'un programme, mais ils ne sont pas suffisants pour s'assurer que le système fonctionne comme un tout, un système. Or en méthodes agiles les développeurs travaillent à partir de sortes de molécules logicielles, nommées comme tout le monde le sait "user stories".
Pour remédier à ce problème d'échelle, il est donc primordial de s'assurer de produire parallèlement des tests d'intégration ET des tests fonctionnels, afin de valider que toutes les parties du système fonctionnent correctement ensemble. Le TDD ne doit surtout pas être utilisé comme une excuse pour ne pas effectuer d'autres tests nécessaires ; mais les développeurs ont très souvent tendance à simuler les échanges entre les différentes fonctions du système d'information. C'est plus rapide à faire, plus léger à exécuter, et cela isole les tests par rapport à la feuille de route des autres fonctions ou des services partenaires. Alors comment vérifier que les tests soient pertinents et répondent aux besoins du projet et de ses objectifs d'assurance qualité, plutôt que de la "definition of done" d'une user story ?
L'introduction de technical stories dans le backlog peut aider à garantir que des tests d'intégration et fonctionnels soient inclus dans le processus de développement, contribuant ainsi à la qualité du produit final. Et comme vous le savez sans doute, les technical stories sont des éléments du backlog qui ne sont pas directement liés à une fonctionnalité visible pour l'utilisateur final, mais qui sont en revanche nécessaires pour assurer la qualité et la maintenabilité du code (on parle de NFR).
Le yin et le yang de la valeur métier et de la qualité logicielle
En l'occurrence, le temps et les efforts nécessaires pour introduire des technical stories dépendront de plusieurs facteurs, notamment :
- La complexité du système - Plus il est complexe, plus il y a de risques d'erreurs d'intégration et de problèmes fonctionnels, ce qui peut nécessiter des tests supplémentaires en cascade.
- La maturité du processus de développement - Si le processus de développement est déjà bien établi et que les tests d'intégration et fonctionnels font déjà partie de la routine des équipes (hello! bonne pratiques ; hello! definition of done) il peut ne pas être nécessaire d'introduire de trop nombreuses technical stories supplémentaires. Tout est une question d'entraînement, ici.
- Les compétences de l'équipe de développement - Si l'équipe de développement a déjà une expertise en matière de tests d'intégration et fonctionnels, cela peut réduire le temps et les efforts nécessaires pour introduire des technical stories. Cette fois-ci, c'est l'expérience qui prime, d'où l'importance de varier les profils.
Il est fortement recommandé de prévoir une "proportion suffisante" de technical stories dans chaque backlog de sprint, pour développer et exécuter en continu les tests supplémentaires nécessaires… à condition qu'elles aient été dûment détaillées ! Les stories ne sont pas des guidelines ni des principes généraux : chaque développeur doit pouvoir identifier l'aspect du test et en connaître les tenants (données en entrée) et les aboutissants (résultats attendus). Cependant, l'effort consacré aux NFR peut inclure du temps de formation poiur l'équipe sur les meilleures pratiques en matière de tests, ou encore un investissement dans des outils de test efficaces, afin d'améliorer la productivité du cycle de développement.
Et en pratique ?..
Malheureusement, je n'ai pas trouvé de statistiques précises sur le pourcentage de temps à allouer à la production de tests unitaires, d'intégration ou fonctionnels. D'autre part, la couverture de code n'est pas non plus un indicateur toujours pertinent, par exemple du fait de l'oubli de cas d'utilisation marginaux ou d'intrants corrompus. La répartition du temps et des efforts consacrés aux tests peut donc varier considérablement en fonction des objectifs, des contraintes et des pratiques spécifiques de chaque projet. Alors que certaines méthodologies de développement logiciel, telles que le TDD et le Behavior Driven Development (BDD) mettent l'accent sur l'importance des tests logiciels, c'est la maîtrise de la Test Automation Pyramid de Mike Cohn qui guidera le mieux vos pas dans la recherche de l'équilibre idéal.
Comme on l'a dit, dans le cadre du TDD, les nombreux tests unitaires sont écrits avant le code de production ; ce qui signifie que les développeurs passent une partie importante de leur temps à écrire des tests, avant même d'avoir produit de la valeur métier. L'avantage du BDD est que les tests sont (supposément) écrits en langage naturel et utilisent des scénarios d'utilisation compréhensibles, pour garantir que les exigences métier soient satisfaites. Et dans le cadre d'une pyramide agile de l'automatisation des tests, une grande proportion des tests sont des tests unitaires, ils sont suivis en proportions moindres de tests d'intégration et de tests fonctionnels automatisés. Sachez que son pendant, la pyramide traditionnelle, inverse ces priorités en privilégiant les tests fonctionnels, pour ne garder que les tests unitaires essentiels ; mais ce sont les plus lourds.
Très bien… Quel est le problème, alors ?
En pratique, pléthore de tests, c'est autant de code à maintenir. Les évolutions fonctionnelles (les contrats d'échange) ont donc un impact démultiplié par le nombre de contrôles effectués, d'autant plus que l'on aura identifié de nombreux cas d'utilisation marginaux. Ce qui signifie que si le périmètre de la fonction n'a pas suffisamment été approfondi dans la phase d'étude, on va avoir une charge de travail plus difficile à prédire. Incidemment, le problème sera plus important si les tests fonctionnels sont déportés du côté des clients, au lieu d'être directement reliés aux algorithmes eux-mêmes.
Essayons de formuler cela sous la forme d'un conseil étonnant : écrivez vos propres tests, et laissez vos utilisateurs vous simuler le plus possible.
D'accord ; mais est-ce toujours de l'intégration, alors ?.. Est-on toujours agile, quand on s'isole dans une bulle de perfection ? Sans vouloir compromettre la qualité du produit, on se bornera à énoncer que la poursuite de la perfection peut sérieusement entraver l'agilité, en limitant la collaboration et la communication avec le client et les autres membres de l'équipe de développement. Cela peut également ralentir le processus de développement, en consacrant aux tests trop de temps et de ressources au détriment de la livraison de valeur métier. Comme souvent, on gagne beaucoup à développer une culture de l'équilibre, pour ne pas perdre les objectifs d'adaptabilité en cours de route.