TDD and BDD - Pros and Сons - Redwerk

Au cours des 12 dernières années, notre entreprise a réalisé avec succès des dizaines de projets, petits et grands. Pendant ce temps, le processus de développement a été considérablement révolutionné. Il y a cinq ans, le flux de travail ressemblait à ceci : nous écrivions d’abord le code, puis, si nous avions suffisamment de temps, nous créions un ensemble de tests unitaires pour le code existant. La couverture de code était inférieure à 50 %. Comme nous pouvons le constater aujourd’hui, une telle approche présentait un certain nombre de lacunes majeures. Auparavant, les développeurs omettaient souvent la logique incohérente, et le code contenant des erreurs était envoyé au serveur de test. Il convient de mentionner que ce code de mauvaise qualité n’a jamais atteint la production, car le manque de tests assistés par ordinateur était compensé par le travail de haute qualité du département QA. La revue de code était également d’une grande aide, car elle n’était exécutée que par des développeurs seniors ayant des décennies d’expérience dans ce domaine. Les clients étaient toujours satisfaits du produit final qu’ils voyaient en production, mais le processus entraînait souvent des tickets qui ne répondaient pas aux attentes du département QA et étaient renvoyés aux développeurs. Parallèlement, une nouvelle exigence de codage est entrée en vigueur. Elle stipulait qu’un pourcentage élevé du code devait être couvert par des tests unitaires. En tenant compte de tout ce qui précède, il y a plusieurs années, notre entreprise a commencé à mettre en œuvre un certain nombre de techniques et d’utilitaires de test assistés par ordinateur comme Selenium. De plus, nous utilisons des outils d’intégration continue comme Jenkins pour presque tous les projets. Si le code commité ne passe pas les tests unitaires, il ne sera pas déployé sur le serveur de test, et tous les développeurs recevront un rapport correspondant par e-mail. Examinons donc de près les différentes méthodes de test assisté par ordinateur, leurs avantages et leurs inconvénients.
Tests unitaires et TDD

Tests unitaires et TDD : avantages et inconvénients

Le test unitaire est une méthode utilisée pour tester la logique des blocs de code séparés (classes, composants) sur lesquels ils reposent lors de leur fonctionnement. En réalité, les tests unitaires sont exécutés automatiquement et représentent un petit bloc de code pour tester la précision des sorties attendues d’un ou plusieurs composants. Ces composants sont testés séparément de leurs dépendances dans un test d’isolation : base de données, stockages, systèmes de fichiers, réseaux, etc. Le développement piloté par les tests (TDD) est une méthodologie de développement basée sur l’écriture de petits tests assistés par ordinateur pour le code (tests unitaires). Grâce à l’utilisation de cette méthodologie, nous obtenons un ensemble complet de tests unitaires qui peuvent être exécutés à tout moment lorsque nous avons besoin de vérifier si le code de l’application fonctionne correctement. Le TDD est utilisé presque universellement par les entreprises qui utilisent des méthodes de développement Agile. Notez qu’en TDD, nous écrivons le test en premier (et non la réalisation en code !) Après le premier lancement, ce test ne répond pas à toutes les exigences techniques. Ensuite, un développeur doit implémenter la fonctionnalité minimale dans le code pour s’assurer que le test passe avec succès. Après cela, le refactoring et les améliorations du code ont lieu.

Le bref slogan du développement piloté par les tests peut se résumer à « Rouge-Vert-Refactor » :

  • « Rouge » : créez un test unitaire et exécutez-le pour constater qu’il échoue
  • « Vert » : implémentez la logique dans un code pour que le test soit réussi
  • « Refactor » : améliorez le code, pour éviter les duplications, améliorer l’architecture afin que le test soit complété avec succès

Associé aux tests écrits avant l’exécution réelle de la logique dans le code, le TDD garantit la plus haute qualité d’un produit et aide toute l’équipe de développement à se concentrer sur la création d’un code simple et compréhensible. Mais comme les tests unitaires se concentrent sur le concept interne du code de l’application, les développeurs externes auront du mal à comprendre le concept derrière l’application. De plus, de nouvelles difficultés surviennent avec l’évaluation du concept et de la couverture de code, ainsi qu’avec la qualité des tests unitaires avant les tests d’intégration. C’est pourquoi toute la responsabilité des tests unitaires n’incombe pas au département QA, mais aux développeurs, car les tests unitaires gèrent des blocs de code de bas niveau et nécessitent la connaissance de l’architecture logicielle de l’application. De plus, les testeurs ne devraient pas travailler avec des tests unitaires étant donné les itérations entre la création d’un test par un développeur et l’implémentation pour sa réussite.

Inconvénients du TDD

  • certains développeurs considèrent toujours les tests comme une perte de temps totale
  • la nécessité de créer du code supplémentaire dans les tests augmente le temps nécessaire au développement
  • les tests peuvent être facilement mis en œuvre de manière incorrecte, ils vérifieront le travail de classes spécifiques et de leurs méthodes, mais pas du système en général

Avantages du TDD

  • un ensemble de tests unitaires assure un retour d’information constant sur le fonctionnement de chaque élément du système
  • les tests unitaires font partie d’un projet et ne peuvent pas être obsolètes, contrairement à une documentation spécifique qui sera oubliée tôt ou tard
  • le TDD exige une compréhension claire de la logique de fonctionnement du code, car sans une compréhension claire des résultats attendus, on ne peut pas exécuter le test
  • la qualité du code du projet augmente, car un développeur peut refactoriser le code à tout moment et vérifier l’exactitude de son exécution
  • le nombre de tickets renvoyés par le QA aux développeurs diminue, car une partie des erreurs dans le code est vérifiée par des tests unitaires
  • un ensemble de tests fonctionne comme un filet de sécurité, car lors de la correction de bugs, les développeurs créent des tests pour vérifier si les problèmes se répéteront ou non. Ainsi, la probabilité d’apparition de bugs similaires diminue

Enfin, nous ne pouvons manquer de mentionner deux méthodes pratiques fondamentales appliquées dans le code des tests unitaires d’intégration : le mocking et le stubbing. Si vous cherchez ces mots dans un dictionnaire, vous verrez que le nom « mock » signifie « fait pour l’imitation ». Le mocking est principalement utilisé dans les tests unitaires car l’objet a souvent des dépendances sous la forme d’autres objets complexes. Pour isoler le comportement d’un objet testé, vous pouvez remplacer ses dépendances par des mocks qui simulent le comportement des dépendances réelles. C’est une technique utile car il est irréalisable d’inclure la majeure partie des objets réels dans le test unitaire. Pour résumer, le mocking est un processus de création d’objets qui simulent le comportement des dépendances d’objets réels. Parfois, nous pouvons considérer le mocking comme l’inverse du stubbing. Un stub est un arrêt. Ou en d’autres termes, un objet simulé « minimum » ou vide, souvent sans logique ni comportement. Cela signifie que le stub est créé pour aider le test à s’exécuter avec succès. Cela permet aux développeurs de vérifier si les objets testés interagissent correctement avec le mock ou l’utilisent.

Regardons un exemple :

Nous avons souvent besoin de créer une fausse base de données dans les tests unitaires pour tester un objet qui a une DAO dans ses dépendances. En créant un stub, nous imitons facilement la base de données car nous créons une structure de données pour stocker les données. Nous pouvons maintenant créer facilement une DAO sous forme de dépendance à l’objet service testé qui stockera et supprimera des entrées d’un stub de base de données en mémoire factice. Cela nous permettra d’exécuter le test avec succès. Mais avant cela, nous ne pouvons pas tester le comportement général d’un objet testé, au cas où il n’y aurait que 3 entrées avec un ensemble spécifique de valeurs de champ dans la base de données. À ces fins, un stub régulier ne suffit pas, nous devons donc créer un mock et y ajouter des données spécifiques. Après cela, nous spécifierons les assertions dans un test unitaire pour vérifier le comportement du service dans un cas de test avec des collections de données spécifiques. Nous devrions également souligner que le fait que tous les tests unitaires soient réussis ne signifie pas que l’application fonctionne bien en général. Pour vérifier le fonctionnement général de l’application, vous avez besoin de tests de niveau supérieur (ou de tests d’intégration). Les tests fonctionnels (ou d’intégration) considèrent la logique du projet comme un fil conducteur fonctionnel unique. Ils représentent une interaction complète d’objets internes et externes (composants) dans le but d’atteindre le comportement attendu d’une application testée. Les tests fonctionnels sont des tests de haut niveau, et si le code les réussit, cela signifie que l’application fonctionne bien. Un échec, en revanche, signifie que le code ne fournit pas la fonctionnalité attendue.
Avantages et inconvénients du développement piloté par le comportement - Redwerk

Développement piloté par le comportement (BDD) : avantages et inconvénients

Le développement piloté par le comportement (BDD) est basé sur le TDD, mais le TDD se concentre sur les processus internes du logiciel et la précision de l’exécution du code (tests unitaires), tandis que le BDD place les exigences et la valeur commerciale du logiciel au sommet des priorités du logiciel (tests d’acceptation). Pendant ce temps, les tests d’acceptation sont souvent modélisés selon les User Stories et les critères d’acceptation. Ces tests sont normalement décrits en mots simples afin que les personnes extérieures à l’industrie informatique (comme les actionnaires, les analystes commerciaux, les ingénieurs QA et les chefs de projet) les comprennent mieux. Le BDD garantit que le processus de développement du produit a vu le jour grâce à la création de tests. Ces premiers tests doivent décrire la fonctionnalité attendue d’un produit et le comportement du logiciel. Ensuite, la réalisation de la fonctionnalité du produit est exécutée en termes de ces tests. C’est très pratique, nous utilisons donc le BDD pour les tests d’acceptation. De là, nous pouvons supposer que le BDD et le TDD se complètent, car ils représentent une approche différente pour résoudre des problèmes similaires. La gestion du développement étant accomplie par un test, et le processus faisant passer chaque composant « du rouge au vert », ce qui signifie qu’il échoue d’abord (aucune fonctionnalité) mais passe ensuite avec succès (sa fonctionnalité est conforme à une spécification). Grâce à la mise en avant des User Stories lors de la composition des tests en BDD, le résultat final répond aux attentes du client, car il est préférable de décrire le modèle de comportement de l’utilisateur, ses actions et les fonctionnalités dont il pourrait avoir besoin avant de commencer le développement. Notre équipe utilise Python Cucumber et Gherkin pour écrire et exécuter nos User Stories. Gherkin est un langage spécifique au domaine, lisible par le monde des affaires. Il peut être utilisé pour décrire le schéma de comportement de l’utilisateur de manière claire et audible en le divisant en nombreux scripts variés. Chaque script représente une User Story distincte. Chaque ligne d’un tel script est une exigence du logiciel qui est mappée à la fonction qui s’exécute en langage Python. Grâce à ce type d’intégration, nous pouvons utiliser le BDD pour vérifier les aspects fonctionnels de l’expérience utilisateur avec une application de navigateur. Par exemple, nous utilisons l’outil d’automatisation de navigateur Selenium avec des liaisons Python pour exécuter nos tests d’acceptation pour l’interface utilisateur à partir de Gherkin. Chaque User Story est vérifiée dans la dernière version d’une application dans le navigateur, et Python envoie des instructions à un navigateur pour émuler différentes activités utilisateur (par exemple, des clics), avec vérification ultérieure du résultat de cette action selon un ensemble de critères (message d’exécution réussie, erreur de validation, etc.). Comme nous l’avons mentionné ci-dessus, le BDD nécessite la création du script des actions de l’utilisateur en premier lieu. C’est pourquoi, tout d’abord, nous créons un script dans lequel nous décrivons des exemples de situations pour chacun de nos composants. La création du logiciel est effectuée en dernier lieu.

Voici à quoi ressemble notre cycle de développement :

  1. Écrivez un script en Gherkin
  2. Exécutez le script et réalisez que rien ne fonctionne correctement

    2.1 Identifiez les situations où ce script doit fonctionner

    2.2 Commencez à vérifier ces situations et réalisez que rien ne fonctionne correctement

    2.3 Identifiez et implémentez la fonctionnalité minimale nécessaire pour que tous les exemples passent le test

  3. Exécutez tout une fois de plus. En cas de nouvelles erreurs, retournez au point 2.1
  4. Le script fonctionne ! Commencez à créer un nouveau script couvrant la majeure partie des exigences

Dan Nort a été le premier à énoncer l’approche BDD en affirmant que cette méthode est là pour éliminer les problèmes du TDD

Inconvénients du BDD :

  • nécessite une compréhension approfondie d’un plus grand nombre de concepts, ce qui ne permet pas de recommander le BDD à un développeur junior avant qu’il ne comprenne complètement le concept TDD
  • s’agissant d’un concept, le transformer en une pratique technique ou le connecter à un ensemble d’outils revient à le gâcher

Avantages du BDD

  • l’équipe commerciale et les développeurs démontrent un véritable travail d’équipe et permettent aux bonnes personnes de discuter des bonnes choses au bon moment
  • oblige l’entreprise à justifier la priorité des fonctionnalités car elle en montre la valeur réelle
  • permet aux équipes de développement de se concentrer sur les fonctionnalités prioritaires de l’entreprise grâce à une meilleure compréhension
  • les développeurs se disputeront rarement avec l’équipe commerciale pour écrire certaines fonctionnalités avant d’autres
  • grâce à la langue qui utilise une base de connaissances commune, l’équipe commerciale et les développeurs travailleront sur un même projet et resteront sur la même longueur d’onde
  • il vous aide à vous concentrer sur les besoins de l’utilisateur et le comportement attendu au lieu de plonger immédiatement dans tous les détails d’implémentation
  • peut aider les équipes à se concentrer spécifiquement sur les détails de la fonctionnalité et à tester ce qui est important au lieu de simplement créer des tests pour tout le code
  • nécessite une croissance constante dans la compréhension des exigences du produit, ce qui facilite le développement d’applications en constante évolution
  • amène les gens à travailler en étroite collaboration, en particulier lorsqu’il s’agit de développeurs et de membres des équipes commerciales, ce qui permet de normaliser le niveau de compréhension de la zone problématique et la précision de l’implémentation
  • ne vous permet pas de vous perdre dans les piles de documentation et de code obsolètes
  • les équipes sont plus confiantes dans leur travail et ont tendance à en prévoir le développement

Conclusion

Pour conclure, nous tenions simplement à ajouter que le plus important est de comprendre pourquoi et comment appliquer différentes méthodes et outils de test assisté par ordinateur sans écrire de tests pour rien. Une mise en œuvre appropriée des méthodes assistées par ordinateur de test et de développement basée sur l’exécution de tests a une influence significative sur le processus de développement en général et sur la qualité du produit final.