Les pythons et les concombres, main dans la main (troisième partie)

Mer 25 novembre 2009

disclaimer : avant que tu fasses la remarque, je sais parfaitement que ni les pythons, ni les concombres n'ont de main. C'est une allégorie, un licence poétique, une métaphore, voilà.

Pierre de Rosette

Lors des deux articles précédents, j'avais à la fois évoqué la méthodologie "développement dirigé par les tests" et montré un exemple d'implémentation en Python. Mais nous avions aussi constaté que ces tests, très utiles au développeur, ne disaient généralement rien dire au "client" ou à la personne pour laquelle l'application est faite (utilisateur). On pourra raconter ce qu'on voudra à Monsieur Lambda, il ne comprendra pas ces classes et ces méthodes, que ce soit écrit en Python, en Java, en C, en Intercal...

Il manque une Pierre de Rosette entre le technicien et le non-technicien. Cette Pierre de Rosette existe, sous un formalisme "humainement lisible" :

Feature: Addition
    In order to avoid silly mistakes
    As a math idiot
    I want to be told the sum of two numbers

    Scenario Outline: Add two numbers
        Given I have entered 2 into the calculator
        And I have entered 3 into the calculator
        When I press add
        Then the result should be 5 on the screen

C'est en anglais, certes. Mais on peut remarque qu'il y a une certaine "structure" dans ce texte.

[Feature:] Addition
    [In order to] avoid silly mistakes
    [As] a math idiot
    [I want to] be told the sum of two numbers

    [Scenario Outline:] Add two numbers
        [Given] I have entered 2 into the calculator
        [And] I have entered 3 into the calculator
        [When] I press add
        [Then] the result should be 5 on the screen

Les mots encadrés sont facilement repérables en tant que "mots-clés". Il est assez facile de fabriquer un programme qui repère les phrases commençant par "Given" ou "I want to" et de déterminer le comportement du programme. Tiens, si on l'écrit en Français, c'est encore mieux :

<pre

<code Fonctionnalité: Addition Pour éviter des erreurs idiotes En tant que nul en maths Je veux qu'on me donne la somme de deux nombres

Plan du Scénario: Ajouter deux nombres Soit le nombre 2 entré dans la calculatrice Et le nombre 3 entré dans la calculatrice Lorsque j'appuie sur ajout Alors le résultat doit être 5 à l'écran </code

</pre

On a là un document lisible pour le péquin moyen, le client lambda. Un document en Français, qui décrit le comportement de l'application dans une langue que peut comprendre un "non-ordinateurien". Et comme il a une structure, et que cette structure est "facilement" analysable par un programme informatique.

Ce programme, tu t'en doutes, il existe :

Cucumber

Cucumber est un outil écrit en Ruby, qui implémente le formalisme décrit ci-dessus. Comme indiqué sur le site de Cucumber, on peut écrire des spécifications en langage "naturel", qui sont ensuite traduites en tests en Ruby, et on peut alors procéder aux cycles classiques du TDD (cf. l'article précédent), à savoir : écriture du test qui doit échouer, implémentation jusqu'à ce que le test passe au vert.

Youpi ! Formidable !

Ça veut dire que, durant la phase "d'analyse" (ou de préparation à l'implémentation), le client ou son représentant peut rencontrer l'équipe de développement, se mettre autour d'une table et parler un langage commun. On imagine bien la rédaction commune de ces documents de spécification, qui peuvent utiliser le langage métier du client et que l'équipe de développement peut rapidement transformer en tests que le programme va devoir faire passer.

Seulement... Il y a une chose qui me dérange, personnellement. Le langage de développement que je privilégie, c'est Python. Et faire fonctionner Cucumber avec Python, c'est peut-être possible, mais ça nécessite l'installation de RubyPython, etc... je me suis dit qu'il devait y avoir une autre solution. Analyser la structure d'un texte en Python, c'est une chose très accessible, il y a des dizaines d'outils qui savent faire ça.

J'ai cherché et j'ai trouvé une implémentation 100% en Python, appelée Freshen.

Freshen s'installe comme plugin à l'exccccccellent Nose (j'en ai parlé là déjà). Tout développeur Python qui se respecte et qui pratique TDD se doit d'utiliser Nose, déjà. Lancer nosetests, c'est comme un réflexe. Normalement, tu devrais même avoir un alias pour lancer cette commande plus vite, plus souvent. Voilà.

Freshen n'est pas parfait, loin de là. Certaines fonctionnalités de Cucumber ne sont pas implémentées. Certes. Mais une chose m'embêtait plus que les autres : les fichiers de spécification doivent être écrites en anglais. Ça fait même plus que m'embêter : ça emmerde aussi n'importe quel "non-ordinateurien" qui ne parlerait pas anglais. Ça fait un paquet de monde. Il faut imaginer un demandeur, un décideur, qui n'a pas forcément besoin ou envie de parler l'anglais, faire confiance à une spécification écrite en anglais. Pour lui, le bénéfice de cette méthode, c'est zéro : autant lui filer l'impression d'un code Perl, ça aura autant d'intérêt pour lui.

Mais... s'il est en Python, je peux le comprendre. Si je peux le comprendre et que sa licence le permet, c'est que je peux l'adapter pour qu'il puisse causer Français. Et Espagnol. Et Basque. Et Espéranto. Et même Klingon !

J'ai donc fait (du mieux que j'ai pu) un clone du dépôt sur GitHub et adapté Freshen pour qu'il puisse causer la langue de Molière.

Désormais, on peut lancer nose avec les arguments suivants :

$ nosetests --with-freshen --lang=fr

et les fichiers de "features" (fonctionnalités) sont interprétés dans la langue de son choix.

Les langues disponibles sont strictement identiques à celles implantées dans Cucumber, puisque j'ai poussé le vice en "empruntant" le fichier de langues de Cucumber (qui définit les mots-clés anglais et leurs traductions) pour alimenter les traductions de Freshen. Y'en a 40. Pas mal non ?

Ceux et celles qui veulent tester ce Freshen qui fleure bon désormais le camembert, la paëlla et le gloubiboulga, la branche s'appelle "i18n" et les retours sur cette implémentation sont les bienvenus.