Quelques rappels et généralités sur les tests, Python et le TDD
Test unitaire vs. test d'intégration
Question récurrente s'il en est ! Et à laquelle on trouve finalement peu de réponses claires, tant chaque développeur a son point de vue sur cette affaire. Voici une vision personnelle, qui vaut ce qu'elle vaut, mais qui vous permettra de vous faire une idée simple :
- Un test unitaire est un procédé destiné à s'assurer du bon fonctionnement d'une unité de programme, généralement de petite taille.
- Un test d'intégration vise à s'assurer du bon fonctionnement de la mise en œuvre conjointe de plusieurs unités de programme.
Finalement, cette distinction est souvent une affaire de sémantique. Dans la vraie vie, les développeurs Django écrivent des tests. Unitaires, d'intégration… Tout ceci se retrouve généralement « mélangé » dans les modules de tests.
Python et les tests
Bien qu'il ne constitue pas la seule alternative possible, Python vient avec un module de testing assez complet et pratique nommé unittest.
unittest
permet de définir des cas de tests, avec notamment un jeu d'assertions assez complet et pratique.
Tester en Python avec unittest
Un exemple vaut mieux que mille mots : voyons comment utiliser unittest
sur un cas simple.
import unittest
def is_even(nbr):
"""
Cette fonction teste si un nombre est pair.
"""
return nbr % 2 == 0
class MyTest(unittest.TestCase):
def test_is_even(self):
self.assertTrue(is_even(2))
self.assertFalse(is_even(1))
self.assertEqual(is_even(0), True)
if __name__ == '__main__':
unittest.main()
.
--------------------
Ran 1 test in 0.001s
OK
Comme le montre cet exemple, un test case (qui est en fait plutôt, au fond, un use case qui peut regrouper plusieurs test cases…) est une classe qui hérite de la classe TestCase
définie dans le module unittest
que nous avons importé pour l'occasion.
Une classe de test (TestCase
) est composée de méthodes définissant chacune un test. Chacune de ces méthodes définit à son tour différentes assertions, ou vérifications. Notre classe MyTest
comporte donc un « test », qui réalise 3 vérifications.
Bien entendu, en Python comme dans beaucoup d'autres langages, on définit généralement les classes de test dans des fichiers différents des classes métiers ou des scripts eux-mêmes. L'exemple présenté ci-dessus regroupe le code et les tests dans le même fichier pour des besoins de simplification.
Doctest : une façon originale et pratique de tester…
Python possède un outil très appréciable, souple, pratique et agréable pour inclure des tests simples dans des méthodes ou fonctions sans avoir à définir de classes de test particulières. Cet outil standard s'appelle doctest.
Grâce à doctest
, le programmeur peut inclure des tests unitaires directement dans la documentation des fonctions et méthodes !
Ici encore, voyons comment cela fonctionne sur un exemple : reprenons notre exemple précédent en intégrant les tests directement dans la documentation de la fonction.
def is_even(nbr):
"""
Cette fonction teste si un nombre est pair.
>>> is_even(2)
True
>>> is_even(1)
False
>>> is_even(0)
True
"""
return nbr % 2 == 0
if __name__ == '__main__':
import doctest
doctest.testmod()
Et voilà ! Notre fonction simple est testée ! À l'exécution, rien n'est affiché : normal, il n'y a pas d'erreur… Introduisons une coquille pour voir ce qui se passera :
def is_even(nbr):
"""
Cette fonction teste si un nombre est pair.
>>> is_even(2)
True
>>> is_even(1)
False
>>> is_even(0)
True
"""
return nbr % 2 == 1 # <= Ce code est buggé !
if __name__ == '__main__':
import doctest
doctest.testmod()
**********************************
File "./petit_pepaire.py", line 7, in __main__.is_even
Failed example:
is_even(2)
Expected:
True
Got:
False
**********************************
File "./petit_pepaire.py", line 9, in __main__.is_even
Failed example:
is_even(1)
Expected:
False
Got:
True
**********************************
File "./petit_pepaire.py", line 11, in __main__.is_even
Failed example:
is_even(0)
Expected:
True
Got:
False
**********************************
1 items had failures:
3 of 3 in __main__.is_even
***Test Failed*** 3 failures.
TDD : Test driven development, développement dirigé par les tests
Le TDD (test driven development ou développement dirigé par les tests en français) est à la mode… tout le monde en parle, mais peu de développeurs l'utilisent réellement et efficacement.
Et pourtant, agilité rime avec TDD : quand on veut livrer souvent, vite et bien, l'une des meilleures stratégies est de se laisser guider par le contrat de service de nos composants : autrement dit, on commence par écrire les tests, on les regarde échouer lamentablement, puis on écrit le code et… on espère qu'ils n'échouent plus.
Qu'à cela ne tienne : dans la suite de ce cours, nous tâcherons de faire les choses correctement, et d'adopter cette approche aussi souvent que possible !
Testez vos connaissances
- Oui
- Non, c'est comme se demander si l'écran est plus important que le disque dur : les deux sont importants.
- Il permettent de définir plus de tests à la fois.
- C'est une méthode très simple pour définir rapidement et de façon commode quelques tests d'une méthode ou d'une fonction.
- Le module
doctest
fait partie des modules de base de Python, ce qui est un avantage par rapport àunittest
.
- Dans F
- Dans un autre fichier que F
setUp()
d'une classe de test case unittest Python ?- Cette méthode est dépréciée aujourd'hui, et n'a plus d'utilité.
- À initialiser les variables qui seront utiles dans les différents tests de la classe de test.
- Elle permet de libérer la mémoire après l'exécution des tests.
Soit le code suivant. Que se passe-t-il quand on exécute le script ?
import unittest
class Ginette:
def dire_bonjour(self, nom):
return "Bonjour {nom}, je suis Ginette".format(nom=nom)
class GinetteTest(unittest.TestCase):
def setUp(self):
self.ginou = Ginette()
def test_bonjour_quelquun(self):
self.assertEqual(self.ginou.dire_bonjour('Simone'), "Bonjour Berthe, je suis Ginette")
if __name__ == '__main__':
unittest.main()
- Rien, le script n'est pas valide.
- On obtient un rapport de test positif qui dit que tout s'est bien passé.
- Un seul test est exécuté.
- Deux tests sont exécutés.
- Trois tests sont exécutés.
- On obtient un rapport de test négatif.