Le Document-Driven Developpement (DDD)
| Author: | Tarek Ziadé <tarek@ziade.org> |
|---|---|
| Date: | $Date: 2007-02-21$ |
| License: | CC-By-SA 2 license |
Objectifs:
En un mot: comment développer agile en Python
Objectifs secrets (ne pas montrer ce slide):

Que veux dire le mot agile ?
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/ [1])
| [1] | Le site ressemble à celui d'une secte, mais c'est une bonne secte ![]() |
Les principes agiles peuvent s'appliquer à tout processus répétitif:
TDD == Test-Driven Development == Développement Dirigé par les Tests

Le TDD avec Python
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'
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
Ca plante, changeons la fonction:
>>> def division(a, b): ... if b == 0: ... return 0 ... return a / b
relancons le test:
>>> test_division2() 'OK'
Je change la fonction, je revalide les tests
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
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
Le TDD offre:
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"
Bruce a raison.
Le TDD avec Python
Python est batteries included. On y trouve:
Il existe plein d'autres outils tiers de tests
unittest fourni des outils pour l'écriture des tests:
TestCase fourni:
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
Les tests peuvent être:
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')
Lancement des tests:
dabox:~ tarek$ python test_division.py .. ------------------------------------------------------------------ Ran 2 tests in 0.000s OK
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)
Pour aller plus loin:
-> Comment organiser les tests dans un projet Python
Sans les tests:
Avec les tests:
Avec les tests:

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 exemples sont directement disponibles
Le code devient dur à lire
Solution: séparer les doctests dans un fichier texte
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
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')
doctests + tests classiques == campagne de test

Le Document-Driven Development
Principe:
Les doctests séparés peuvent aussi être des documents
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
Promiscuité de la documentation
La documentation évolue en même temps que le code
Le développeur utilise la doc. pour concevoir les tests
La documentation du projet est conçue en partie par ce biais
Un doctest doit rester un document: ne pas le noyer dans des
test fixtures (mise en place pour les tests)
Les doctests montrent des exemples publics de code
Les tests unitaires classiques s'occupent du reste
-> Demo: construction d'un module de calcul
Pour aller plus loin:
-> utilisation du reSTructuredText dans les documents
Questions ?
Sortie le 16 aout: