Débuter en Test Driven Development (TDD) peut être perturbant. Par où commencer ? Je propose dans cet article de commencer un voyage au pays du TDD à travers des exemples.

Il y a quelques temps, je m'étais posé la question suivante : en combien de coups en moyenne une partie de Dôbutsu Shôgi se termine-t-elle ? Combien de parties différentes sont possibles ? Le calcul étant complexe et mon expérience du jeu pas très longue, j'avais commencé un simulateur de parties où deux IAs pouvaient s'affronter suivant plusieurs schémas[1].

Shogi_king.jpg

J'ai commencé le TDD il y a 6 ans. Mes premiers pas ont été hésitants, et j'apprends toujours à améliorer ma technique. Entre temps, les techniques de tests elles-mêmes évoluent, se ramifient.

Avec cet article, je reprends l'exercice du début pour constater si j'aborderais le sujet à présent comme je l'ai abordé la première fois.

Par quoi commencer ?

Afin de programmer en TDD, une première étape essentielle est d'avoir un framework de tests. Heureusement, il en existe pour la plupart, si ce n'est tous, des langages de programmation populaires.

Mon étude se faisant en Python, il me faut un framework de tests pour Python. Afin de limiter les installations et vous permettre de me suivre facilement, je vais utiliser le framework de base présent dans le module simplement appelé unittest[2].

Avoir un framework de tests n'est pas suffisant. Le TDD fonctionne par une série cyclique d'étapes qui demandent à lancer les tests très souvent. Il faut donc s'intégrer dans un environnement qui pourra permettre d'itérer rapidement.

Il est tout à fait possible d'ouvrir un terminal et de travailler en ligne de commande, il est aussi possible de travailler avec un plugin Sublime Text[3], ou bien encore dans Eclipse avec PyDev et PyUnit.

Pour vérifier que l'environnement fonctionne, le premier test à faire est très simple, mais essentiel.

    import unittest


    class TestCase(unittest.TestCase):
        def test_fail(self):
            self.fail()

Sauvez ce fichier avec une extention .py puis lancez les tests. La ligne de commande pour lancer ce fichier s'il se nomme doubutsugame_tests.py est python -m unittest doubutsugame_tests.

Vous devriez obtenir quelque chose comme :

    F
    ======================================================================
    FAIL: test_fail (doubutsugame_tests.TestCase)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "doubutsugame_tests.py", line 6, in test_fail
        self.fail()
    AssertionError: None

    ----------------------------------------------------------------------
    Ran 1 test in 0.000s

    FAILED (failures=1)

Ce test échoue. C'est voulu. Un test doit toujours commencer par échouer. Ce premier test nous permet de vérifier qu'en cas d'erreur, notre environnement de tests nous reporte bien une erreur.

Pour le moment, nous avons un test qui échoue. Ça vaut le coup de le transformer en test qui réussi en changeant self.fail() par self.assertTrue(true). N'avancez pas plus loin tant que votre environnement de tests n'est pas fonctionnel. C'est important. Tous vos tests partiront du principe que cette partie fonctionne correctement ; vous devrez avoir confiance en ce système.

Note : avec un environnement comme PyUnit, vous pouvez configurer le lanceur de test pour se déclencher à chaque modification d'un script Python. Si votre base de tests est assez courte à lancer, cela permet d'itérer vraiment rapidement, en ayant constament à l'écran les résultats du dernier lancement.

Dôbutsu Shôgi ?

Le Shôgi, ou encore échecs japonais, est un jeu passionnant mais pas si simple d'accès. Dans son histoire, il a été joué sur des plateaux de diverses tailles avec des pions de divers types.

Une joueuse professionnelle a eu l'idée d'en faire une version d'initiation sur un tablier de 4 par 3 cases et quatre types de pions, dont l'un peut être promu. C'est le Dôbutsu Shôgi, dont je vous invite à consulter les règles pour plus de détails.

Un petit jeu facile à transporter et intéressant à jouer. Les règles étant simples, cela fait un bon candidat pour l'exercice.

Revenons aux tests

Le jeu qui nous intéresse se joue donc sur un tablier contenant des cases, on y pose des pions qui sont ensuite déplacés alternativement par deux joueurs, jusqu'à ce que l'un des lions soit capturé.

On pourrait se lancer dans un début d'analyse objet pour essayer de modéliser tout cela avant de se lancer dans le code. Ça pourrait donner quelque chose comme ça (fait très rapidement).

diagramme_001.png

Il manque plein de choses et... est-ce correct ? Aucune idée. J'ai le sentiment que non, mais comme le but ici est d'utiliser une autre méthodologie, laissons ce diagramme de côté[4]

Le cycle TDD tel qu'indiqué sur Wikipedia est le suivant :

  1. Écrire un premier test ;
  2. Vérifier qu'il échoue, afin de vérifier que le test est valide ;
  3. Écrire juste le code suffisant pour passer le test ;
  4. Vérifier que le test passe ;
  5. Puis refactoriser le code, c'est-à-dire l'améliorer tout en gardant les mêmes fonctionnalités.

Il est donc exclu d'écrire le moindre code qui ne soit pas un test. C'est ici le premier obstacle qu'il va falloir franchir. Bon nombre de programmeurs trouveront évident de devoir implémenter au moins une classe Board comme un ensemble de Pièces de jeu. Tout d'abord car c'est ce que disent les règles du jeu, et ensuite car cela permet de tester « quelque chose ».

Mais ce n'est pas un test. Il nous faut absolument débuter par un test.

Quel test ? Je vous propose d'y réfléchir d'ici le prochain article.

Notes

[1] La réponse est depuis sur Wikipedia version anglaise, mais le but de l'exercice est de faire du TDD

[2] Un autre framework de tests populaire pour Python est nose

[3] Python Test Runner; si vous programmez sous Windows, ma branche non intégrée offre son support.

[4] La modélisation reste un très bon outil, et réfléchir à des modèles et scénarios est très intéressant. Cependant, nous allons utiliser ici le TDD, uniquement le TDD.