14 mai 2022
Comme dans tout projet logiciel sérieux, afin d'augmenter la qualité de celui-ci, il est nécessaire de mettre en place des tests automatisés et de les exécuter automatiquement. Cela permet de s'assurer que les développements futurs ne vont pas casser la base de code actuelle. Mettre en place des tests unitaires dans la base de code actuelle permettra de s'assurer que les features principales de notre projet sont fonctionnelles.
Découvrons ensemble la librairie unittest de Python !
Unittest est un framework de test inclus dans la bibliothèque standard de Python, c'est-à-dire qu'il n'est pas nécessaire de l'installer en tant que package externe.
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_fail(self):
# test will fail
self.assertTrue(False)
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
Cet exemple de code couvre ce dont vous avez le plus besoin de connaître lors de votre usage quotidien de la librairie.
Pour lancer les tests, il suffit de se placer à la racine du projet et d'exécuter la commande :
python -m unittest -v
Le paramètre -v
permettra d'avoir plus de verbosité sur la console.
Cela fournira le résultat suivant avec le paramètre -v
❯ python -m unittest -v
test_fail (test_module1.TestStringMethods) ... FAIL
test_isupper (test_module1.TestStringMethods) ... ok
test_split (test_module1.TestStringMethods) ... ok
test_upper (test_module1.TestStringMethods) ... ok
======================================================================
FAIL: test_fail (test_module1.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
File "~/path/to/test_folder/test_module1.py", line 14, in test_fail
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)
Cela fournira le résultat suivant sans le paramètre -v
❯ python3 -m unittest
F...
======================================================================
FAIL: test_fail (test_module1.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
File "~/path/to/test_folder/test_module1.py", line 14, in test_fail
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)
Il est préférable utiliser le paramètre -v
lorsque les tests sont lancés sur votre propre machine. Dans un pipeline CI/CD (Gitlab CI, Github Actions, Jenkins ou autre) il sera préférable de ne pas utiliser le paramètre -v.
Il existe deux méthodes optionnelles qu'il convient de connaître et qui s'avèrent extrêment utile. Il s'agit des méthodes setUp (signifie mettre en place) et tearDown (signifie démolir) :
setup
permet de définir du code qui va être exécuté avant chaque testtearDown
permet de définir du code qui va être exécuté après chaque testDurant les tests, on utilisera setUp et tearDown afin de par exemple configurer des dossiers, mettre le projet ou la base de donnée dans un état connu avant les tests etc...
Voici un exemple avec l'utilisation de setUp et tearDown :
import unittest
class TestStringMethods(unittest.TestCase):
def setUp(self):
print("setup")
def tearDown(self):
print("tearDown")
def test_upper(self):
print("test_upper")
self.assertEqual("foo".upper(), "FOO")
def test_isupper(self):
print("test_isupper")
self.assertTrue("FOO".isupper())
self.assertFalse("Foo".isupper())
if __name__ == "__main__":
unittest.main()
Après lancement des tests en mode verveux avec python -m unittest -v
, nous avons cet affichage sur console :
test_isupper (test_module1.TestStringMethods) ... setup
test_isupper
tearDown
ok
test_upper (test_module1.TestStringMethods) ... setup
test_upper
tearDown
ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
NB : Remarquez que les méthodes setUp et tearDown sont utilisées avant et après chaque test case.
Les asserts sont des méthodes qui sont utilisées pour vérifier (ou affirmer) ou non la validité d'un test. Voici un exemple d'utilisation d'assert :
import unittest
class SimpleTest(unittest.TestCase):
def test1(self):
self.assertEqual(4 + 5,9)
def test2(self):
self.assertNotEqual(5 * 2,10)
def test3(self):
self.assertTrue(4 + 5 == 9,"The result is False")
def test4(self):
self.assertTrue(4 + 5 == 10,"assertion fails")
def test5(self):
self.assertIn(3,[1,2,3])
def test6(self):
self.assertNotIn(3, range(5))
if __name__ == '__main__':
unittest.main()
Une liste exhaustive des assert est présentées sur ce https://docs.python.org/3/library/unittest.html#assert-methods
unittest.TestCase
python -m unittest -v