====== JPF'07 ====== Le Document-Driven Developpement (DDD) :Author: Tarek Ziadé :Date: $Date: 2007-02-21$ :License: CC-By-SA 2 license .. contents: Qui suis-je ? ============= - Développeur Python depuis 2000 - Auteur d'articles et de livres sur Python - Président de l'AFPY - Passionné par les méthodologies **agiles** Pourquoi cette présentation? ============================ Objectifs: - Sensibiliser les développeurs aux tests - Expliquer comment les tests fonctionnent en Python - Sensibiliser les développeurs au **DDD** - Expliquer pourquoi les **doctests** c'est merveilleux |important| En un mot: comment développer `agile` en Python Pourquoi cette présentation? ============================ Objectifs secrets (ne pas montrer ce slide): - Ridiculiser Java - Déprimer les développeurs Ruby - Rendre les chefs de projet C# jaloux - Augmenter le nombre d'adhérents à l'association Le plan ======= - **Quelques définitions** |smile| - `Partie 1: le TDD avec Python` - `Part 2: les doctests` - `Part 3: le DDD` Quelques définitions ==================== Que veux dire le mot **agile** ? Quelques définitions ==================== Tentative de définition appliqué au développement: **C'est une méthodologie de programmation qui permet de rester réactif aux fréquentes modifications d'une base de code** Viens de l'`Agile Manifesto` (http://agilemanifesto.org/ [#]_) - créé par le GoF (Beck, etc.) et co - XP est une méthode agile populaire .. [#] Le site ressemble à celui d'une secte, mais c'est une bonne secte |wink| Quelques définitions ==================== Les principes agiles peuvent s'appliquer à tout processus répétitif: - coder (-> TDD) - faire de la documentation (-> DDD) - passer la tondeuse - etc. Quelques définitions ==================== TDD == Test-Driven Development == Développement Dirigé par les Tests Le plan ======= - `Quelques définitions` - **Partie 1: le TDD avec Python** |smile| - `Part 2: les doctests` - `Part 3: le DDD` Partie 1 ======== **Le TDD avec Python** - **Les principes du TDD** - `Comment écrire des tests` Les principes du TDD ==================== - Chaque fonction == un ou plusieurs cas d'utilisation - Un cas d'utilisation == un test possible Les principes du TDD ==================== Exemple:: >>> def division(a, b): ... return a / b Essayons ! :: >>> def test_division(): ... if division(4, 2) == 2: ... return 'OK' ... else: ... return 'Le processeur est moisi' >>> test_division() 'OK' Les principes du TDD ==================== Chaque test concerne un aspect du code. En voici un autre:: >>> def test_division2(): ... if division(4, 0) == 0: ... return 'OK' ... else: ... return "C'est discutable..." >>> test_division2() Traceback (most recent call last): ... ZeroDivisionError: integer division or modulo by zero Les principes du TDD ==================== Ca plante, changeons la fonction:: >>> def division(a, b): ... if b == 0: ... return 0 ... return a / b relancons le test:: >>> test_division2() 'OK' Les principes du TDD ==================== - Plusieurs cas testés pour la fonction - Lancer l'ensemble des tests == campagne de tests == `test suite` - La fonction est `mariée` à ses tests *Je change la fonction, je revalide les tests* Les principes du TDD ==================== Encore plus fort: le test est écrit `avant` le code:: >>> def test_average(): ... if average(1, 2, 3) == 2: ... return 'OK' ... else: ... return 'Houston we have a problem' >>> test_average() Traceback (most recent call last): ... NameError: global name 'average' is not defined Les principes du TDD ==================== Le code à présent:: >>> def average(*args): ... return sum(args) / len(args) Le test à nouveau:: >>> test_average() 'OK' -> Les tests devraient être écrits *avant* le code -> Bien souvent ils le sont en même temps ou juste après Les principes du TDD ==================== Le TDD offre: - La **Qualité** - les développeurs révisent naturellement leur code - les refontes de code sont plus faciles - La **non-régression**: on relance les tests à chaque modification - **Une documentation de base**: lire les tests aide à comprendre le code Les principes du TDD ==================== Jean-Charles dit: "Les tests. Quelle perte de temps. C'est juste un jouet pour les langages interprétés" Bruce Eckel dit: *"If it's not tested, it's broken"* Les principes du TDD ==================== Bruce a raison. - Le code non-testé est voué à mourir - Toutes les mesures ont prouvé que l'on passe plus de temps à débugguer du code non-testé qu'à écrire les tests. - Un compilateur valide la syntaxe, pas le fonctionnement. Partie 1 ======== **Le TDD avec Python** - `Les principes du TDD` - **Comment écrire des tests** Comment écrire des tests ======================== Python est *batteries included*. On y trouve: - `unittest`: un framework de test à la **JUnit** - `doctest`: un outil de tests à la **literate-programming-un-peu** |important| Il existe plein d'autres outils tiers de tests Comment écrire des tests ======================== `unittest` fourni des outils pour l'écriture des tests: - une classe `TestCase`, pour les assertions et les `test fixture` (environnement pour le test); - une classe `TestSuite`, pour créér des séquences de tests; - quelques fonctions pour lancer des *test campaign*. Comment écrire des tests ======================== `TestCase` fourni: - deux méthodes pour les `test fixture` - setUp: au début du test - tearDown: après le test - des méhodes pour les assertions: - ``assert_`` - assertEquals - assertRaises - etc.. Comment écrire des tests ======================== Ecrire une suite de tests == dériver de `TestCase`:: >>> import unittest >>> class DivisionTestCase(unittest.TestCase): ... ... def test_one(self): ... self.assertEquals(division(4, 2) , 2) ... ... def test_two(self): ... self.assert_(division(4, 0) == 0) ... Un test == une méthode préfixée de `test` Comment écrire des tests ======================== Les tests peuvent être: - sauvegardés dans des fichiers (e.g. un module) - ajoutés à un `test suite` (e.g. une classe `TestSuite`) - lancés par un `runner` fourni par défaut (e.g. la fonction `main`) Comment écrire des tests ======================== Exemple de module:: import unittest from division import division class DivisionTestCase(unittest.TestCase): def test_one(self): self.assertEquals(division(4, 2) , 2) def test_two(self): self.assert_(division(4, 0) == 0) def test_suite(): suite = unittest.TestSuite() suite.addTests(unittest.makeSuite(DivisionTestCase)) return suite if __name__ == '__main__': unittest.main(defaultTest='test_suite') Comment écrire des tests ======================== Lancement des tests:: dabox:~ tarek$ python test_division.py .. ------------------------------------------------------------------ Ran 2 tests in 0.000s OK Comment écrire des tests ======================== Lancement des tests, avec une erreur:: dabox:~ tarek$ python test_division.py F. ================================================================== FAIL: test_one (__main__.DivisionTestCase) ------------------------------------------------------------------ Traceback (most recent call last): File "test_division.py", line 7, in test_one self.assertEquals(division(4, 2) , 3) AssertionError: 2 != 3 ------------------------------------------------------------------ Ran 2 tests in 0.000s FAILED (failures=1) Partie 1 ======== Pour aller plus loin: -> Comment organiser les tests dans un projet Python Partie 1 ======== Sans les tests: .. figure:: media/sleeping.jpg :align: center :height: 300 :width: 300 Partie 1 ======== Avec les tests: .. figure:: media/happy_1.jpg :align: center :height: 300 :width: 300 Partie 1 ======== Avec les tests: .. figure:: media/happy_2.gif :align: center :height: 300 :width: 200 Le plan ======= - `Quelques définitions` - `Partie 1: le TDD avec Python` - **Partie 2: les doctests** |smile| - `Partie 3: le DDD` Partie 2 ======== Les `doctests` - basés sur le principe du `literate programming` (KNUTH) - utilise le prompt Python Les doctests ============ Exemple de doctest `inline`:: def somme(a, b): """ calcul la somme >>> somme(1, 3) 4 >>> somme(2, 2) 4 """ return a + b Les doctests ============ |smile| Les exemples sont directement disponibles |sad| Le code devient dur à lire Les doctests ============ Solution: séparer les doctests dans un fichier texte - chaque module de code peut avoir son module de doctest - un script `execute` ces modules de tests Les doctests ============ Exemple de doctest séparé. Fichier *calc.py*:: def somme(a, b): """ calcul la somme """ return a + b Fichier *calc.txt*:: >>> from calc import somme >>> somme(1, 3) 4 >>> somme(2, 2) 4 Les doctests ============ Un fichier doctest devient un test unitaire grâce au module `doctest`:: import doctest import unittest def test_suite(): suite.append(doctest.DocFileTest('calc.txt')) return unittest.TestSuite(suite) if __name__ == '__main__': unittest.main(defaultTest='test_suite') Les doctests ============ doctests + tests classiques == campagne de test Le plan ======= - `Quelques définitions` - `Partie 1: le TDD avec Python` - `Partie 2: les doctests` - **Partie 3: le DDD** |smile| Partie 3 ======== Le Document-Driven Development Principe: Les doctests séparés peuvent `aussi` être des documents Documentation ============= Chaque doctest == alternance de code et d'explications:: Module calc Le module calc contient une fonction pour faire des sommes: >>> from calc import somme Cette fonction prends deux paramètres: >>> somme(1, 3) 4 >>> somme(2, 2) 4 Documentation ============= |smile| Promiscuité de la documentation |smile| La documentation évolue en même temps que le code |smile| Le développeur utilise la doc. pour concevoir les tests |smile| La documentation du projet est conçue en partie par ce biais Documentation ============= |important| Un doctest doit rester un document: ne pas le noyer dans des `test fixtures` (mise en place pour les tests) |important| Les doctests montrent des exemples **publics** de code |important| Les tests unitaires classiques s'occupent du reste Documentation ============= -> Demo: construction d'un module de calcul Documentation ============= Pour aller plus loin: -> utilisation du reSTructuredText dans les documents Conclusion ========== Questions ? Ressources ========== - http://docs.python.org (doctest et unittest) - http://programmation-python.org Ressources ========== Sortie le 16 aout: .. figure:: media/guide.png :align: center :height: 300 :width: 200 .. |wink| image:: media/wink.png .. |smile| image:: media/smile.png .. |important| image:: media/important.png .. |love| image:: media/love.png .. |glasses| image:: media/glasses.png .. |sad| image:: media/sad.png