|
@@ -0,0 +1,104 @@
|
|
|
|
|
+Un exemple : on veut écrire une fonction, qui nous renvoie une valeur booléenne indiquant si on est majeur quand on a l'âge passé en paramètre.
|
|
|
|
|
+
|
|
|
|
|
+#### Le test
|
|
|
|
|
+
|
|
|
|
|
+D'abord le test :
|
|
|
|
|
+
|
|
|
|
|
+```python
|
|
|
|
|
+import unittest
|
|
|
|
|
+
|
|
|
|
|
+class TestIsMajor(unittest.TestCase):
|
|
|
|
|
+
|
|
|
|
|
+ def test_is_major(self):
|
|
|
|
|
+ self.assertTrue(is_major(30))
|
|
|
|
|
+ self.assertFalse(is_major(11))
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ unittest.main()
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+La classe `TestIsMajor` hérite de `TestCase` et de ses méthodes. Parmi celles-ci, des *assertions*, qui permettent de s'assurer qu'on obtient bien le résultat voulu.
|
|
|
|
|
+Les deux utilisées ici, `assertTrue()` et `assertFalse()`, veulent s'assurer que l'expression qu'elles reçoivent en paramètre est respectivement vrai ou faux.
|
|
|
|
|
+
|
|
|
|
|
+Les assertions sont souvent groupées à plusieurs dans une méthode, mais on aurait pu aussi bien remplacer `test_is_major()` par deux méthodes distinctes :
|
|
|
|
|
+
|
|
|
|
|
+```python
|
|
|
|
|
+ def test_is_major(self):
|
|
|
|
|
+ self.assertTrue(is_major(30))
|
|
|
|
|
+
|
|
|
|
|
+ def test_is_not_major(self):
|
|
|
|
|
+ self.assertFalse(is_major(11))
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### Première exécution : échec attendu
|
|
|
|
|
+
|
|
|
|
|
+Si on l'exécute tel quel (`python 06-tdd-exemple1.py`), on obtient logiquement une erreur car la fonction `is_major()` n'existe pas :
|
|
|
|
|
+```text
|
|
|
|
|
+E
|
|
|
|
|
+======================================================================
|
|
|
|
|
+ERROR: test_is_major (__main__.TestIsMajor)
|
|
|
|
|
+----------------------------------------------------------------------
|
|
|
|
|
+Traceback (most recent call last):
|
|
|
|
|
+ File "06-tdd1-test.py", line 10, in test_is_major
|
|
|
|
|
+ self.assertTrue(is_major(30))
|
|
|
|
|
+NameError: name 'is_major' is not defined
|
|
|
|
|
+
|
|
|
|
|
+----------------------------------------------------------------------
|
|
|
|
|
+Ran 1 test in 0.000s
|
|
|
|
|
+
|
|
|
|
|
+FAILED (errors=1)
|
|
|
|
|
+```
|
|
|
|
|
+A noter : le `E` isolé en première ligne nous indique que l'unique test a fini en *erreur* (ce qui est confirmé par la dernière ligne avec `errors=1`).
|
|
|
|
|
+
|
|
|
|
|
+#### Petite amélioration...
|
|
|
|
|
+
|
|
|
|
|
+Tentons une première amélioration, et ajoutons ces deux lignes juste au-dessous de la classe, dans le même fichier (normalement on a les **tests et le code à tester dans des fichiers séparés**) :
|
|
|
|
|
+
|
|
|
|
|
+```python
|
|
|
|
|
+def is_major(age):
|
|
|
|
|
+ pass
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Voyons ce qui se passe :
|
|
|
|
|
+```text
|
|
|
|
|
+F
|
|
|
|
|
+======================================================================
|
|
|
|
|
+FAIL: test_is_major (__main__.TestIsMajor)
|
|
|
|
|
+----------------------------------------------------------------------
|
|
|
|
|
+Traceback (most recent call last):
|
|
|
|
|
+ File "06-tdd1-test.py", line 10, in test_is_major
|
|
|
|
|
+ self.assertTrue(is_major(30))
|
|
|
|
|
+AssertionError: None is not true
|
|
|
|
|
+
|
|
|
|
|
+----------------------------------------------------------------------
|
|
|
|
|
+Ran 1 test in 0.000s
|
|
|
|
|
+
|
|
|
|
|
+FAILED (failures=1)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Cela ne semble pas beaucoup mieux, et pourtant, on progresse... Au moins, le test a pu appeler la fonction `is_major()`.
|
|
|
|
|
+
|
|
|
|
|
+Au lieu d'un `E` isolé en première ligne, on a un `F` pour *failure*. On nous indique en bas `failures=1`.
|
|
|
|
|
+
|
|
|
|
|
+On a une `AssertionError` car la première assertion a échoué. La deuxième aussi aurait échoué, mais une méthode dans une classe de test est interrompue à la première assertion qui échoue.
|
|
|
|
|
+
|
|
|
|
|
+#### Résolution :
|
|
|
|
|
+
|
|
|
|
|
+Voici le code qui fera passer le test :
|
|
|
|
|
+```python
|
|
|
|
|
+def is_major(age):
|
|
|
|
|
+ return True if age >= 18 else False
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Résultat :
|
|
|
|
|
+```text
|
|
|
|
|
+.
|
|
|
|
|
+----------------------------------------------------------------------
|
|
|
|
|
+Ran 1 test in 0.000s
|
|
|
|
|
+
|
|
|
|
|
+OK
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Ça a marché, et le succès est matérialisé par le `.` isolé sur la première ligne (il n'y avait qu'un test).
|
|
|
|
|
+Cette convention (`.` pour succès, `F` pour échec, `E` pour erreur) donne un affichage compact, utile quand on a *beaucoup* de tests à faire passer.
|