Introduction
Dans ce laboratoire, vous apprendrez à utiliser le module intégré unittest de Python. Ce module propose un cadre pour organiser et exécuter des tests, ce qui est une partie essentielle du développement logiciel pour garantir que votre code fonctionne correctement.
Vous apprendrez également à créer et exécuter des cas de test de base, ainsi qu'à tester les erreurs et exceptions attendues. Le module unittest simplifie le processus de création de suites de tests, d'exécution des tests et de vérification des résultats. Le fichier teststock.py sera créé au cours de ce laboratoire.
Création de votre premier test unitaire
Le module unittest de Python est un outil puissant qui propose une manière structurée d'organiser et d'exécuter des tests. Avant de plonger dans l'écriture de notre premier test unitaire, comprenons quelques concepts clés. Les fixtures de test (test fixtures) sont des méthodes telles que setUp et tearDown qui aident à préparer l'environnement avant un test et à le nettoyer ensuite. Les cas de test (test cases) sont des unités individuelles de test, les suites de test (test suites) sont des collections de cas de test, et les exécuteurs de test (test runners) sont chargés d'exécuter ces tests et de présenter les résultats.
Dans cette première étape, nous allons créer un fichier de test de base pour la classe Stock, qui est déjà définie dans le fichier stock.py.
- Tout d'abord, ouvrons le fichier
stock.py. Cela nous aidera à comprendre la classeStockque nous allons tester. En regardant le code dansstock.py, nous pouvons voir comment la classe est structurée, quelles sont ses attributs et quelles méthodes elle propose. Pour afficher le contenu du fichierstock.py, exécutez la commande suivante dans votre terminal :
cat stock.py
- Maintenant, il est temps de créer un nouveau fichier nommé
teststock.pyen utilisant votre éditeur de texte préféré. Ce fichier contiendra nos cas de test pour la classeStock. Voici le code que vous devez écrire dans le fichierteststock.py:
## teststock.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()
Décortiquons les éléments clés de ce code :
import unittest: Cette ligne importe le moduleunittest, qui fournit les outils et les classes nécessaires pour écrire et exécuter des tests en Python.import stock: Cela importe le module qui contient notre classeStock. Sans cette importation, nous ne pourrions pas accéder à la classeStockdans notre code de test.class TestStock(unittest.TestCase): Nous créons une nouvelle classe nomméeTestStockqui hérite deunittest.TestCase. Cela fait de notre classeTestStockune classe de cas de test, qui peut contenir plusieurs méthodes de test.def test_create(self): Il s'agit d'une méthode de test. Dans le cadre (framework)unittest, toutes les méthodes de test doivent commencer par le préfixetest_. Cette méthode crée une instance de la classeStock, puis utilise la méthodeassertEqualpour vérifier si les attributs de l'instanceStockcorrespondent aux valeurs attendues.assertEqual: C'est une méthode fournie par la classeTestCase. Elle vérifie si deux valeurs sont égales. Si elles ne sont pas égales, le test échouera.unittest.main(): Lorsque ce script est exécuté directement,unittest.main()exécutera toutes les méthodes de test de la classeTestStocket affichera les résultats.
- Après avoir écrit le code dans le fichier
teststock.py, enregistrez - le. Ensuite, exécutez la commande suivante dans votre terminal pour exécuter le test :
python3 teststock.py
Vous devriez voir une sortie similaire à ceci :
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Le point unique (.) dans la sortie indique qu'un test a réussi avec succès. Si un test échoue, vous verrez un F à la place du point, ainsi que des informations détaillées sur ce qui a mal fonctionné dans le test. Cette sortie vous aide à identifier rapidement si votre code fonctionne comme prévu ou s'il y a des problèmes à corriger.
Élargissement de vos cas de test
Maintenant que vous avez créé un cas de test de base, il est temps d'élargir votre champ de test. Ajouter plus de tests vous aidera à couvrir la fonctionnalité restante de la classe Stock. De cette façon, vous pouvez vous assurer que tous les aspects de la classe fonctionnent comme prévu. Nous allons modifier la classe TestStock pour inclure des tests pour plusieurs méthodes et propriétés.
- Ouvrez le fichier
teststock.py. À l'intérieur de la classeTestStock, nous allons ajouter quelques nouvelles méthodes de test. Ces méthodes testeront différentes parties de la classeStock. Voici le code que vous devez ajouter :
def test_create_keyword_args(self):
s = stock.Stock(name='GOOG', shares=100, price=490.1)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
def test_cost(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s.cost, 49010.0)
def test_sell(self):
s = stock.Stock('GOOG', 100, 490.1)
s.sell(20)
self.assertEqual(s.shares, 80)
def test_from_row(self):
row = ['GOOG', '100', '490.1']
s = stock.Stock.from_row(row)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
def test_repr(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(repr(s), "Stock('GOOG', 100, 490.1)")
def test_eq(self):
s1 = stock.Stock('GOOG', 100, 490.1)
s2 = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s1, s2)
Examinons de plus près ce que chaque test fait :
test_create_keyword_args: Ce test vérifie si vous pouvez créer un objetStocken utilisant des arguments nommés (keyword arguments). Il vérifie que les attributs de l'objet sont correctement définis.test_cost: Ce test vérifie si la propriétécostd'un objetStockrenvoie la bonne valeur, qui est calculée comme le nombre d'actions multiplié par le prix.test_sell: Ce test vérifie si la méthodesell()d'un objetStockmet correctement à jour le nombre d'actions après avoir vendu certaines d'entre elles.test_from_row: Ce test vérifie si la méthode de classefrom_row()peut créer une nouvelle instance deStockà partir d'une ligne de données.test_repr: Ce test vérifie si la méthode__repr__()d'un objetStockrenvoie la représentation sous forme de chaîne de caractères attendue.test_eq: Ce test vérifie si la méthode__eq__()compare correctement deux objetsStockpour voir s'ils sont égaux.
- Après avoir ajouté ces méthodes de test, enregistrez le fichier
teststock.py. Ensuite, exécutez les tests à nouveau en utilisant la commande suivante dans votre terminal :
python3 teststock.py
Si tous les tests réussissent, vous devriez voir une sortie comme celle - ci :
......
----------------------------------------------------------------------
Ran 7 tests in 0.001s
OK
Les sept points dans la sortie représentent chaque test. Chaque point indique qu'un test a réussi avec succès. Donc, si vous voyez sept points, cela signifie que les sept tests ont réussi.
Test des exceptions
Le test est une partie cruciale du développement logiciel, et un aspect important consiste à s'assurer que votre code peut gérer correctement les conditions d'erreur. En Python, le module unittest offre un moyen pratique de tester si des exceptions spécifiques sont levées comme prévu.
- Ouvrez le fichier
teststock.py. Nous allons ajouter quelques méthodes de test conçues pour vérifier les exceptions. Ces tests nous aideront à nous assurer que notre code se comporte correctement lorsqu'il rencontre des entrées invalides.
def test_shares_type(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.shares = '50'
def test_shares_value(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(ValueError):
s.shares = -50
def test_price_type(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.price = '490.1'
def test_price_value(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(ValueError):
s.price = -490.1
def test_attribute_error(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(AttributeError):
s.share = 100 ## 'share' is incorrect, should be 'shares'
Comprenons maintenant comment fonctionnent ces tests d'exceptions.
- La déclaration
with self.assertRaises(ExceptionType):crée un gestionnaire de contexte (context manager). Ce gestionnaire de contexte vérifie si le code à l'intérieur du blocwithlève l'exception spécifiée. - Si l'exception attendue est levée à l'intérieur du bloc
with, le test réussit. Cela signifie que notre code détecte correctement l'entrée invalide et lève l'erreur appropriée. - Si aucune exception n'est levée ou si une autre exception est levée, le test échoue. Cela indique que notre code peut ne pas gérer l'entrée invalide comme prévu.
Ces tests sont conçus pour vérifier les scénarios suivants :
- Définir l'attribut
sharessur une chaîne de caractères devrait lever uneTypeErrorcarsharesdevrait être un nombre. - Définir l'attribut
sharessur un nombre négatif devrait lever uneValueErrorcar le nombre d'actions ne peut pas être négatif. - Définir l'attribut
pricesur une chaîne de caractères devrait lever uneTypeErrorcarpricedevrait être un nombre. - Définir l'attribut
pricesur un nombre négatif devrait lever uneValueErrorcar le prix ne peut pas être négatif. - Tenter de définir un attribut inexistant
share(notez l'absence de 's') devrait lever uneAttributeErrorcar le nom correct de l'attribut estshares.
- Après avoir ajouté ces méthodes de test, enregistrez le fichier
teststock.py. Ensuite, exécutez tous les tests en utilisant la commande suivante dans votre terminal :
python3 teststock.py
Si tout fonctionne correctement, vous devriez voir une sortie indiquant que les 12 tests ont réussi. La sortie ressemblera à ceci :
............
----------------------------------------------------------------------
Ran 12 tests in 0.002s
OK
Les douze points représentent tous les tests que vous avez écrits jusqu'à présent. Il y avait 7 tests à l'étape précédente, et nous venons d'en ajouter 5 de nouveaux. Cette sortie montre que votre code gère les exceptions comme prévu, ce qui est un excellent signe d'un programme bien testé.
Exécution de tests sélectionnés et utilisation de la découverte de tests
Le module unittest en Python est un outil puissant qui vous permet de tester efficacement votre code. Il propose plusieurs méthodes pour exécuter des tests spécifiques ou découvrir et exécuter automatiquement tous les tests de votre projet. Cela est très utile car il vous aide à vous concentrer sur des parties spécifiques de votre code lors des tests ou à vérifier rapidement l'ensemble des tests du projet.
Exécution de tests spécifiques
Parfois, vous pouvez souhaiter exécuter seulement des méthodes de test ou des classes de test spécifiques au lieu de l'ensemble des tests. Vous pouvez y parvenir en utilisant l'option de modèle (pattern) avec le module unittest. Cela vous donne plus de contrôle sur les tests qui sont exécutés, ce qui peut être pratique lorsque vous déboguez une partie particulière de votre code.
- Pour exécuter seulement les tests liés à la création d'un objet Stock :
python3 -m unittest teststock.TestStock.test_create
Dans cette commande, python3 -m unittest indique à Python d'exécuter le module unittest. teststock est le nom du fichier de test, TestStock est le nom de la classe de test, et test_create est la méthode de test spécifique que nous voulons exécuter. En exécutant cette commande, vous pouvez rapidement vérifier si le code lié à la création d'un objet Stock fonctionne comme prévu.
- Pour exécuter tous les tests de la classe
TestStock:
python3 -m unittest teststock.TestStock
Ici, nous omettons le nom de la méthode de test spécifique. Ainsi, cette commande exécutera toutes les méthodes de test de la classe TestStock dans le fichier teststock. Cela est utile lorsque vous voulez vérifier la fonctionnalité globale des cas de test de l'objet Stock.
Utilisation de la découverte de tests
Le module unittest peut découvrir et exécuter automatiquement tous les fichiers de test de votre projet. Cela vous évite le détail de spécifier manuellement chaque fichier de test à exécuter, en particulier dans les projets plus grands avec de nombreux fichiers de test.
- Renommez le fichier actuel pour qu'il suive le modèle de nommage de la découverte de tests :
mv teststock.py test_stock.py
Le mécanisme de découverte de tests dans unittest recherche les fichiers qui suivent le modèle de nommage test_*.py. En renommant le fichier en test_stock.py, nous facilitons la tâche du module unittest pour trouver et exécuter les tests de ce fichier.
- Exécutez la découverte de tests :
python3 -m unittest discover
Cette commande indique au module unittest de découvrir et d'exécuter automatiquement tous les fichiers de test qui correspondent au modèle test_*.py dans le répertoire actuel. Il parcourra le répertoire et exécutera tous les cas de test trouvés dans les fichiers correspondants.
- Vous pouvez également spécifier un répertoire dans lequel rechercher les tests :
python3 -m unittest discover -s . -p "test_*.py"
Où :
-s .spécifie le répertoire de départ pour la découverte (le répertoire actuel dans ce cas). Le point (.) représente le répertoire actuel. Vous pouvez le changer pour un autre chemin de répertoire si vous voulez rechercher des tests dans un autre emplacement.-p "test_*.py"est le modèle pour correspondre aux fichiers de test. Cela garantit que seuls les fichiers dont le nom commence partest_et ayant l'extension.pysont considérés comme des fichiers de test.
Vous devriez voir tous les 12 tests s'exécuter et réussir, comme précédemment.
- Renommez le fichier en son nom d'origine pour assurer la cohérence avec le laboratoire :
mv test_stock.py teststock.py
Après avoir exécuté la découverte de tests, nous renommons le fichier en son nom d'origine pour maintenir la cohérence de l'environnement de laboratoire.
En utilisant la découverte de tests, vous pouvez facilement exécuter tous les tests d'un projet sans avoir à spécifier chaque fichier de test individuellement. Cela rend le processus de test plus efficace et moins sujet aux erreurs.
Résumé
Dans ce laboratoire, vous avez appris à utiliser le module unittest de Python pour créer et exécuter des tests automatisés. Vous avez créé un cas de test de base en étendant la classe unittest.TestCase, écrit des tests pour vérifier le bon fonctionnement des méthodes et propriétés d'une classe, et créé des tests pour vérifier que les exceptions appropriées sont levées en cas d'erreur. Vous avez également appris à exécuter des tests spécifiques et à utiliser la découverte de tests.
Le test unitaire est une compétence fondamentale dans le développement logiciel, assurant la fiabilité et la correction du code. Écrire des tests approfondis permet de détecter les bogues tôt et donne confiance dans le comportement de votre code. Lorsque vous développez des applications Python, envisagez d'adopter une approche de développement piloté par les tests (Test-Driven Development - TDD), en écrivant les tests avant d'implémenter la fonctionnalité pour obtenir un code plus robuste et plus maintenable.