[BDD] Introduction au Behavior Driven Development

Introduction à la BDD

J’ai récemment pu expérimenter au cours d’une mission auprès d’un client la méthodologie BDD, autrement dit « Behaviour Driven Developement » (développement dirigé par le comportement). J’ai trouvé cela fort intéressant, du coup je me suis intéressé au sujet. 

Ce post aura pour objectif de revenir sur la notion de BDD, d’en présenter les principales grandes lignes ainsi que de suivre la méthodologie à travers un exemple.

BDD

Le processus BDD met en avant le langage naturel et les interactions dans le processus de développement logiciel. Les développeurs utilisant le BDD utilisent leur langue maternelle […] pour décrire l’objectif et le bénéfice de leur code. Cela permet aux développeurs de se concentrer sur les raisons pour lesquelles le code doit être créé […]. 
Définition Wikipédia de BDD

La notion de BDD fut introduite par Dan North, en réponse aux nombreuses critiques formulées à l’encontre du TDD (développement dirigé par les tests), en particulier concernant les difficultés de compréhension entre le programmeur et le chargé fonctionnel.

En effet au cours de projets en TDD, il m’est souvent arrivé de me demander par quel test je devais commencer, ce que je devais tester et à contrario ce que je ne devais pas tester, et surtout comment je devais tester. La méthodologie BDD est censée apporter des solutions à ces interrogations. 

Ses principes sont évoqués ci-dessous :

  • Définir les comportements de l’application avec des notations compréhensibles par tous : chargés fonctionnels et développeurs.
  • Chaque morceau de code est implémenté pour satisfaire un comportement de l’application, ceci afin de développer uniquement ce qui est utile à l’application.
  • Les exigences définissent les tests et le comportement de l’application. Elles sont définies à travers un canevas « Etant donné que … Lorsque … Alors ».

Ne vous y trompez pas, BDD ne réinvente pas le TDD, il s’appuie dessus et y ajoute des notions permettant une meilleure compréhension et définissant un fil conducteur pour le développeur.

Concrètement, qu’est-ce que cela donne ?

Imaginons le cas d’une application embarquée dans un automate pour la vente de billet de train. Je veux pouvoir définir la possibilité d’acheter des billets pour Londres.

Le chargé fonctionnel définit les comportements attendus ainsi :

Exigence 1 : 

Etant donné qu’il reste de la place dans le train pour Londres
Et ma carte bancaire est valide
Et j’ai suffisamment d’argent sur mon compte
Lorsque je valide l’achat d’un billet pour Londres avec ma carte bancaire
Alors je reçois une confirmation de la transaction
Et j’obtiens 1 billet pour Londres
Et j’obtiens le reçu de ma carte bancaire
Et mon compte est débité du montant du billet

Exigence 2 :

Etant donné qu’il reste de la place dans le train pour Londres
Et le montant du billet est de 40€
Et j’ai un billet de 100€
Lorsque je veux acheter 1 billet pour Londres avec mon billet
Alors je reçois une confirmation de la transaction
Et je reçois 1 billet pour Londres
Et j’obtiens un remboursement d’une valeur de 60€

Exigence 3 :

Etant donné qu’il reste de la place dans le train pour Londres
Et ma carte bancaire est valide
Et je n’ai pas suffisamment d’argent sur mon compte
Lorsque je valide l’achat d’un billet pour Londres avec ma carte bancaire
Alors je reçois un message m’indiquant que la transaction a échoué

Exigence 4 :

Etant donné qu’il reste de la place dans le train pour Londres
Et le montant du billet est de 40€
Et j’ai un billet de 20€
Lorsque je veux acheter 1 billet pour Londres avec mon billet
Alors je reçois un message m’invitant à compléter le reste de la somme ou à annuler la transaction

La question que le développeur se pose est « par où commencer » ? 

La méthodologie BDD préconise de prioriser en choisissant le comportement le plus important.
Ici, dans le cas de l’automate, j’ai pris l’option de l’opération la plus fréquente; soit le paiement par carte. Je ne retiendrai donc que les exigences 1 et 3.
De manière générale, la décision est prise en concertation avec le chargé fonctionnel. Le choix se porte le plus souvent sur la valeur business la plus importante. La question simple à se poser, est : Quelle est la fonctionnalité la plus importante qui manque actuellement à mon application ?. Bien entendu cela dépend du contexte du projet, dans le cas de mon automate, la fonctionnalité la plus importante est le paiement par carte de par sa fréquence.

L’exigence est alors traduite au travers d’un test d’intégration par le développeur. Un test qui échoue, bien entendu (principe même du TDD). 

 

Le nom du test doit être compréhensible par tous, on construit alors son nom par une phrase décrivant le comportement du test tel « Acheter un billet pour Londres avec ma carte bancaire et avec assez d’argent sur mon compte ». La phrase doit couvrir l’intégralité du comportement. 

Chaque étape du scénario devra être implémentée par la suite par le développeur. Les étapes « Etant donné que » (ou « Given ») permettent de définir le contexte initial du test, l’étape « Lorsque » (ou « When ») indique le déclencheur du comportement et les étapes « Alors » (ou « Then ») sont chargées de vérifier si l’application a bien répondu au déclencheur. 

 

J’utilise l’outil SpecFlow, disponible dans les packages nugget, qui fournit une interface pour suivre la méthodologie BDD à partir de Visual Studio. Il fournit également le squelette de la méthode de test (voir ci-dessus) que le développeur devra implémenter. Remarquez le nom des méthodes qui reprenne l’intitulé des étapes. 

Je ne vais délibérément pas détailler l’utilisation de SpecFlow, ce n’est pas le sujet, cela se fera peut-être dans un autre post.

A ce moment, l’application ne possède pas encore d’entité Banque pour indiquer qu’une carte bancaire est valide, confirmer une transaction, ou bien d’entité Gare pour permettre l’achat d’un billet. On utilise alors des bouchons (ou mocksvoir le post suivant pour plus d’information) afin de combler l’absence de ces entités. 

Le test d’intégration est toujours en échec. 

Je cherche à faire passer mon test d’intégration, pour cela je mets en place des tests unitaires propres à mon entité Banque. Ces entités prendront la responsabilité d’un comportement.
La convention veut que l’on place le mot préfix « doit » (should en anglais) devant le nom des méthodes de test. On pourrait ainsi lire un test par « Mon entité Banque doit valider ma carte bancaire » pour la méthode « DoitValiderMaCarteBancaire » de ma classe « BanqueTest ». Cette convention a un objectif: celui d’inciter le développeur à ne pas se disperser. Il doit en effet constamment se poser la question: est-ce que ce test s’applique à cette entité ?

Je dois à présent créer mon entité Banque afin que mon test unitaire passe également. Je le développe avec dans l’esprit l’idée de faire toujours au plus simple (principe YAGNI). Si mon entité Banque doit accéder à une couche de donnée « BanqueDAL », je mets en place des tests unitaires pour ma couche d’accès aux données, je créé ensuite l’entité « BanqueDAL » afin de satisfaire aux tests.

Une fois mes tests unitaire passés, j’effectue une refactorisation de mon code si besoin, et je lance mon test d’intégration. Celui-ci ne passe pas; chose normale vu que je n’ai pas non plus mon entité Gare. 

J’itère ainsi à créer mes tests unitaire d’entité jusqu’à ce que mon test d’intégration passe

Lorsque mon test d’intégration passe, je lance tous mes tests, et j’observe ou non des erreurs.

Si j’en obtiens, il y a trois possibilités :

  • J’ai introduit un bug, donc je le corrige
  • Le comportement attendu est toujours pertinent, mais s’est déplacé ailleurs. Je déplace le test et je le modifie éventuellement
  • Le comportement n’est plus correct, donc je supprime le test

Dans le cas où je n’ai pas de régression, j’itère sur un nouveau comportement, et donc un nouveau test d’intégration. Par exemple celui de l’exigence 3.

Le cycle se poursuit jusqu’à couvrir tous les comportements de l’application. 

Pour en savoir plus :

http://dannorth.net/introducing-bdd 

http://referentiel.institut-agile.fr/bdd.html

http://www.specflow.org

http://fr.wikipedia.org/wiki/Behavior_Driven_Development

http://blog.stevensanderson.com/2010/03/03/behavior-driven-development-bdd-with-specflow-and-aspnet-mvc/