Introduction
La nature dynamique de Python rend les tests d'une importance cruciale pour la plupart des applications. Il n'y a pas de compilateur pour détecter vos bugs. Le seul moyen de trouver des bugs est d'exécuter le code et de s'assurer d'avoir essayé toutes ses fonctionnalités.
Assertions
L'instruction assert est une vérification interne pour le programme. Si une expression n'est pas vraie, elle lève une exception AssertionError.
Syntaxe de l'instruction assert.
assert <expression> [, 'Message de diagnostic']
Par exemple.
assert isinstance(10, int), 'Attendu un int'
Elle ne devrait pas être utilisée pour vérifier les entrées de l'utilisateur (c'est-à-dire les données saisies dans un formulaire web ou autre chose). Son but est plutôt de vérifications internes et d'invariants (conditions qui devraient toujours être vraies).
Programmation par contrat
Connue également sous le nom de Conception par Contrat, l'utilisation extensive d'assertions est une approche pour concevoir des logiciels. Elle stipule que les concepteurs de logiciels devraient définir des spécifications d'interface précises pour les composants du logiciel.
Par exemple, vous pourriez placer des assertions sur toutes les entrées d'une fonction.
def add(x, y):
assert isinstance(x, int), 'Expected int'
assert isinstance(y, int), 'Expected int'
return x + y
Vérifier les entrées permettra immédiatement de détecter les appelants qui n'utilisent pas les arguments appropriés.
>>> add(2, 3)
5
>>> add('2', '3')
Traceback (most recent call last):
...
AssertionError: Expected int
>>>
Tests en ligne
Les assertions peuvent également être utilisées pour effectuer des tests simples.
def add(x, y):
return x + y
assert add(2,2) == 4
De cette manière, vous incluez le test dans le même module que votre code.
Avantage : Si le code est manifestement cassé, les tentatives d'importer le module provoqueront une erreur.
Cela n'est pas recommandé pour les tests exhaustifs. C'est plutôt un "test de fumée" de base. La fonction fonctionne-t-elle avec n'importe quel exemple? Si ce n'est pas le cas, alors quelque chose est certainement cassé.
Module unittest
Supposons que vous ayez du code dans simple.py.
## simple.py
def add(x, y):
return x + y
Maintenant, supposons que vous vouliez le tester. Créez un fichier de test séparé comme ceci dans /home/labex/project/test_simple.py.
## test_simple.py
import simple
import unittest
Ensuite, définissez une classe de test.
## test_simple.py
import simple
import unittest
## Remarquez qu'elle hérite de unittest.TestCase
class TestAdd(unittest.TestCase):
...
La classe de test doit hériter de unittest.TestCase.
Dans la classe de test, vous définissez les méthodes de test.
## test_simple.py
import simple
import unittest
## Remarquez qu'elle hérite de unittest.TestCase
class TestAdd(unittest.TestCase):
def test_simple(self):
## Test avec des arguments entiers simples
r = simple.add(2, 2)
self.assertEqual(r, 5)
def test_str(self):
## Test avec des chaînes de caractères
r = simple.add('hello', 'world')
self.assertEqual(r, 'helloworld')
*Important : Chaque méthode doit commencer par test.
Utilisation de unittest
Il existe plusieurs assertions intégrées dans unittest. Chacune d'entre elles affirme une chose différente.
## Affirmer que expr est vraie
self.assertTrue(expr)
## Affirmer que x == y
self.assertEqual(x,y)
## Affirmer que x!= y
self.assertNotEqual(x,y)
## Affirmer que x est proche de y
self.assertAlmostEqual(x,y,places)
## Affirmer que callable(arg1,arg2,...) lève exc
self.assertRaises(exc, callable, arg1, arg2,...)
Ce n'est pas une liste exhaustive. Il y a d'autres assertions dans le module.
Exécution de unittest
Pour exécuter les tests, convertissez le code en un script.
## test_simple.py
...
if __name__ == '__main__':
unittest.main()
Ensuite, exécutez Python sur le fichier de test.
$ python3 test_simple.py
F.
========================================================
ÉCHEC: test_simple (__main__.TestAdd)
--------------------------------------------------------
Traceback (most recent call last):
File "testsimple.py", line 8, in test_simple
self.assertEqual(r, 5)
AssertionError: 4!= 5
--------------------------------------------------------
Exécuté 2 tests en 0.000s
ÉCHEC (échecs=1)
Commentaire
L'écriture d'un bons tests unitaires est un art et peut devenir assez complexe pour les grandes applications.
Le module unittest dispose d'un grand nombre d'options relatives aux exécuteurs de tests, à la collecte des résultats et à d'autres aspects des tests. Consultez la documentation pour plus de détails.
Outils de test tiers
Le module unittest intégré a l'avantage d'être disponible partout - c'est une partie de Python. Cependant, de nombreux programmeurs le trouvent également assez verbeux. Une alternative populaire est pytest. Avec pytest, votre fichier de test se simplifie comme suit :
## test_simple.py
import simple
def test_simple():
assert simple.add(2,2) == 4
def test_str():
assert simple.add('hello','world') == 'helloworld'
Pour exécuter un test, il suffit de taper une commande comme python -m pytest. Il trouvera et exécutera ensuite tous les tests. Le module peut être installé à l'aide de pip install pytest.
Il y a bien plus à pytest que cet exemple, mais il est généralement assez facile de commencer si vous décidez d'essayer.
Dans cet exercice, vous explorerez les mécanismes de base de l'utilisation du module unittest de Python.
Dans les exercices précédents, vous avez écrit un fichier stock.py qui contenait une classe Stock. Pour cet exercice, on suppose que vous utilisez le code écrit pour l'Exercice 7.9 relatif aux propriétés typées. Si, pour une raison quelconque, cela ne fonctionne pas, vous pouvez vouloir copier la solution de Solutions/7_9 dans votre répertoire de travail.
Exercice 8.1 : Écrire des tests unitaires
Dans un fichier séparé test_stock.py, écrivez un ensemble de tests unitaires pour la classe Stock. Pour vous aider à commencer, voici un petit fragment de code qui teste la création d'instances :
## test_stock.py
import unittest
import stock
class TestStock(unittest.TestCase):
def test_create(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
if __name__ == '__main__':
unittest.main()
Exécutez vos tests unitaires. Vous devriez obtenir une sortie qui ressemble à ceci :
.
Ran 1 tests in 0.000s
OK
Une fois que vous êtes satisfait que cela fonctionne, écrivez d'autres tests unitaires qui vérifient les points suivants :
- Vérifiez que la propriété
s.costrenvoie la valeur correcte (49010.0) - Vérifiez que la méthode
s.sell()fonctionne correctement. Elle devrait décrémenter la valeur des.sharesen conséquence. - Vérifiez que l'attribut
s.sharesne peut pas être défini sur une valeur non entière.
Pour la dernière partie, vous devrez vérifier qu'une exception est levée. Un moyen simple de le faire est avec du code comme ceci :
class TestStock(unittest.TestCase):
...
def test_bad_shares(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.shares = '100'
Sommaire
Félicitations! Vous avez terminé le laboratoire sur les tests. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.