La librairie standard fournit une libraire de tests: unittest. L'api est très classique et très proche de l'api XUnit:
Les cas de test doivent hériter de unittest.TestCase et toute méthode commençant par test sera considéré comme un test.
Si le TestCase définit une méthode setUp, elle sera exécutée avant chaque test et sert à préparer l'environnement de test (DB, fichiers, instancier des objets).
Si le TestCase définit une méthode tearDown, elle sera exécutée après chaque test et sert à nettoyer l'environnement de test (effacer une DB).
1 import random
2 import unittest
3
4 class TestSequenceFunctions(unittest.TestCase):
5
6 def setUp(self):
7 self.seq = range(10)
8
9 def test_shuffle(self):
10 # make sure the shuffled sequence does not lose any elements
11 random.shuffle(self.seq)
12 self.seq.sort()
13 self.assertEqual(self.seq, range(10))
14
15 # should raise an exception for an immutable sequence
16 self.assertRaises(TypeError, random.shuffle, (1,2,3))
17
18 def test_choice(self):
19 element = random.choice(self.seq)
20 self.assertTrue(element in self.seq)
21
22 if __name__ == '__main__':
23 unittest.main()
Python 2.7 a vu arriver de nombreuses nouveautés dans unittest, en commençant par de nouvelles méthodes d'assertions:
Les messages d'erreurs ont aussi étés grandement améliorés comme lors de la comparaison entre 2 strings qui montre maintenant la différence entre elles.
Ces nouveautés ont étés backportés dans les version 2.4, 2.5 et 2.6 de python dans le paquet unittest2.
1 # access the exception object
2 with self.assertRaises(TypeError) as cm:
3 do_something()
4
5 exception = cm.exception
6 self.assertEqual(exception.error_code, 3)
1 list1 = range(5)
2 list2 = range(2, 7)
3 self.assertEqual(list1, list2)
Sortie de l'exécution:
1 ======================================================================
2 FAIL: test (__main__.TestCase)
3 ----------------------------------------------------------------------
4 Traceback (most recent call last):
5 File "sources/test_equal.py", line 7, in test
6 self.assertEqual(list1, list2)
7 AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,... != [2, 3, 4, 5, 6, 7, 8, 9, 10, 1...
8
9 First differing element 0:
10 0
11 2
12
13 - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
14 ? ------
15
16 + [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
17 ? ++++++++
Unittest jusqu'à python-2.7 ne proposait pas de système de découverte automatique de test. Depuis il est possible d'utiliser la découverte de test en ligne de commande avec:
python -m unittest discover
Unittest est suffisant pour faire la plupart des tests mais le lancement des tests est un point noir quand l'on veut lancer rapidement certains tests . De plus nose fournit des fonctionalités intéressantes comme:
1 class Sample(object):
2
3 def test(self, a, b, c):
4 print "Divider", b - c
5 return a / (b - c)
6
7 import unittest
8
9 class TestCase(unittest.TestCase):
10
11 def test_first(self):
12 self.assertEqual(Sample().test(1, 4, 2), 1/2)
13
14 def test_second(self):
15 self.assertEqual(Sample().test(0, 2, 1), 0)
16
17 def test_third(self):
18 self.assertEqual(Sample().test(4, 3, 3), None)
19
20 if __name__ == '__main__':
21 unittest.main()
Exécution avec python:
$ python test_output.py Divider 2 .Divider 1 .Divider 0 E ====================================================================== ERROR: test_third (main.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_output.py", line 18, in test_third self.assertEqual(Sample().test(4, 3, 3), None) File "test_output.py", line 5, in test return a / (b - c) ZeroDivisionError: integer division or modulo by zero ---------------------------------------------------------------------- Ran 3 tests in 0.002sFAILED (errors=1)
Exécution avec nosetests
$ nosetests ..E ====================================================================== ERROR: test_third (test_output.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/lothiraldan/Labo/presentations/PythonTest/sources/test_output.py", line 18, in test_third self.assertEqual(Sample().test(4, 3, 3), None) File "/Users/lothiraldan/Labo/presentations/PythonTest/sources/test_output.py", line 5, in test return a / (b - c) ZeroDivisionError: ZeroDivisionError: integer division or modulo by zero -------------------- >> begin captured stdout << --------------------- Divider 0 --------------------- >> end captured stdout << ---------------------- ---------------------------------------------------------------------- Ran 3 tests in 0.005sFAILED (errors=1)
1 EMAIL_REGEXP = r'[\S.]+@[\S.]+'
2
3 def test_email_regexp():
4 # a regular e-mail address should match
5 assert re.match(EMAIL_REGEXP, 'test@nowhere.com')
6
7 # no domain should fail
8 assert not re.match(EMAIL_REGEXP, 'test@')
Sortie avec nosetests:
1 ======================================================================
2 FAIL: nose_example.test_email_regexp
3 ----------------------------------------------------------------------
4 Traceback (most recent call last):
5 File "/Library/Python/2.5/site-packages/nose-1.1.2-py2.5.egg/nose/case.py", line 197, in runTest
6 self.test(*self.arg)
7 File "/Users/lothiraldan/Labo/presentations/PythonTest/sources/nose_example.py", line 13, in test_email_regexp
8 assert not re.match(EMAIL_REGEXP, 'test@nowhere')
9 AssertionError
10
11 ----------------------------------------------------------------------
12 Ran 1 test in 0.001s
13
14 FAILED (failures=1)
Table of Contents | t |
---|---|
Exposé | ESC |
Source Files | s |
Slide Numbers | n |
Notes | 2 |
Help | h |