python pour débutant
- Épisode # 290 Side Hustles for Data Scientists
- 10 plugins WordPress WordPress gratuits pour augmenter le nombre d'opinions
- Transcription de l'épisode 19: Python unittest avec Robert Collins
- Traitement du langage naturel avec le package NLTK de Python – Real Python
- Utilisation du crochet wp_footer pour modifier votre pied de page (sans changement de thème)
Je pense à pytest comme le run-n'importe quoi, pas de passe-partout, pas d'api requise, utilisez-ce-que-si-vous-avez-une-raison-non-à-tester.
C’est vraiment là que les tests s’amusent.
Comme pour les précédentes introductions sur ce site, je vais passer en revue une vue d’ensemble, puis un exemple simple, puis jetter pytest dans mon projet markdown.py. Je couvrirai également les appareils, la découverte de test et l’exécution de tests avec pytest.
Contenu
Pas de passe-partout, pas d'api requise
Le doctest
et unittest viennent tous les deux avec Python.
Ils sont assez puissants en eux-mêmes, et je pense que vous devriez au moins connaître ces frameworks et apprendre à les utiliser au moins sur quelques exemples de jouets, car cela vous donne un framework mental pour visualiser d'autres frameworks de test.
Avec unittest, un fichier de test très basique pourrait ressembler à ceci:
importer unittest
de nécessité_math importer multiplier
Classe TestUM (unittest.TestCase):
def test_numbers_3_4 (auto):
self.assertEqual (multiplier (3,4), 12)
importation Test de l'unité de inut_math importation multiplier classe TestUM(Test de l'unité.Cas de test): def test_numbers_3_4(soi): soi.affirmerEqual( multiplier(3,4), 12) |
Le style de dériver de unittest.TestCase
est quelque chose qui partage le moins avec ses homologues xUnit comme JUnit.
Je ne veux pas entrer dans l’histoire des frameworks de style xUnit. Cependant, il est instructif de savoir que l’héritage est très important dans certaines langues pour que le cadre de test fonctionne correctement.
Mais c'est Python. Nous avons des capacités d’introspection et d’exécution très puissantes et très peu d’informations dissimulées. Pytest en profite.
Un test identique à celui ci-dessus pourrait ressembler à ceci si nous retirons le passe-partout:
de nécessité_math importer multiplier
def test_numbers_3_4 ():
affirmer (multiplier (3,4) == 12)
de inut_math importation multiplier def test_numbers_3_4(): affirmer( multiplier(3,4) == 12 ) |
Oui, trois lignes de code. (Quatre, si vous incluez la ligne vide.)
Il n'y a pas besoin de importer non négligeable
.
Il n'y a pas besoin de dériver de Cas de test
.
Il n'y a pas besoin de spécial self.assertEqual ()
, puisque nous pouvons utiliser la version intégrée de Python affirmer
déclaration.
Cela fonctionne dans pytest. Une fois que vous commencez à écrire de tels tests, vous ne voudrez plus revenir en arrière.
Cependant, vous pouvez déjà avoir un tas de tests écrits pour doctest ou unittest.
Pytest peut être utilisé pour exécuter des doctestes et des unittests.
Il prétend également supporter certains tests d’essais tordus (bien que je n’aie pas essayé cela).
Vous pouvez étendre pytest en utilisant les plugins que vous extrayez du Web ou écrire vous-même.
Je ne vais pas couvrir les plugins dans cet article, mais je suis sûr d’y revenir dans un prochain article.
Vous verrez parfois pytest appelé py.test.
J'utilise cette convention:
pytest: le projet
py.test: l'outil en ligne de commande qui exécute pytest
Je ne suis pas sûr que ce soit tout à fait exact selon la façon dont les gens de pytest.org utilisent les termes.
exemple pytest
En utilisant le même module inutile_math.py que j'ai écrit dans le
docto intro,
Voici un exemple de code de test permettant de tester la fonction «multiplier».
de nécessité_math importer multiplier
def test_numbers_3_4 ():
affirmer multiplier (3,4) == 12
def test_strings_a_3 ():
affirmer multiplier ('a', 3) == 'aaa'
de inut_math importation multiplier def test_numbers_3_4(): affirmer multiplier(3,4) == 12 def test_strings_a_3(): affirmer multiplier('une',3) == 'aaa' |
Course à pied
Pour exécuter pytest, les deux appels suivants sont identiques:
python -m pytest test_um_pytest.py
py.test test_um_pytest.py
python –m pytest test_um_pytest.py py.tester test_um_pytest.py |
Et avec verbose:
python -m pytest -v test_um_pytest.py
py.test -v test_um_pytest.py
python –m pytest –v test_um_pytest.py py.tester –v test_um_pytest.py |
Je vais utiliser py.test
, car il est plus court à taper.
Voici un exemple exécuté avec et sans verbose:
> py.test test_um_pytest.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4
collectionner … collectionné 2 articles
test_um_pytest.py ..
=========================== 2 passés en 0.05 secondes ================== =========
> py.test -v test_um_pytest.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4 – C: python27 python.exe
collectionner … collectionné 2 articles
test_um_pytest.py:12: test_numbers_3_4 PASSED
test_um_pytest.py:15: test_strings_a_3 PASSED
=========================== 2 passés en 0.02 secondes ================= =========
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 |
> py.tester test_um_pytest.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 la collecte ... collecté 2 articles test_um_pytest.py .. =========================== 2 passé dans 0,05 secondes =========================== > py.tester –v test_um_pytest.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 – C:python27python.EXE la collecte ... collecté 2 articles test_um_pytest.py:12: test_numbers_3_4 PASSÉ test_um_pytest.py:15: test_strings_a_3 PASSÉ =========================== 2 passé dans 0,02 secondes =========================== |
luminaires pytest
Même si unittest nous permet d’avoir la configuration et le démontage, pytest le prolonge un peu.
Nous pouvons ajouter du code spécifique pour exécuter:
- au début et à la fin d'un module de code de test (setup_module / teardown_module)
- au début et à la fin d'une classe de méthodes de test (setup_class / teardown_class)
- autre style des projecteurs de niveau classe (installation / démontage)
- avant et après un appel de fonction test (setup_function / teardown_function)
- avant et après un appel de méthode de test (setup_method / teardown_method)
Nous pouvons également utiliser des luminaires de style pytest, recouverts d'écrous et de boulons de luminaires pytest.
J’ai modifié notre code de test simple avec des appels de fixture et ajouté quelques instructions print afin que nous puissions voir ce qui se passe.
Voici le code:
de nécessité_math importer multiplier
def setup_module (module):
print ("setup_module module:% s"% module .__ name__)
def teardown_module (module):
print ("module de module de décomposition:% s"% module .__ nom__)
def setup_function (fonction):
print ("setup_function function:% s"% function .__ name__)
def teardown_function (fonction):
print ("functiondown_function:% s"% function .__ name__)
def test_numbers_3_4 ():
print 'test_numbers_3_4 <============================= Code de test réel'
affirmer multiplier (3,4) == 12
def test_strings_a_3 ():
print 'test_strings_a_3 <============================= Code de test réel'
affirmer multiplier ('a', 3) == 'aaa'
classe TestUM:
def setup (auto):
print ("classe d'installation: TestStuff")
def démontage (auto):
print ("classe de démontage: TestStuff")
def setup_class (cls):
print ("setup_class class:% s"% cls .__ name__)
def teardown_class (cls):
print ("classedowndown_class:% s"% cls .__ nom__)
def setup_method (auto, méthode):
print ("méthode setup_method:% s" méthode% .__ nom__)
def teardown_method (auto, méthode):
print (méthode "teardown_method:% s" méthode% .__ nom__)
def test_numbers_5_6 (auto):
print 'test_numbers_5_6 <============================= Code de test réel'
affirmer multiplier (5,6) == 30
def test_strings_b_2 (auto):
print 'test_strings_b_2 <============================= Code de test réel'
affirmer multiplier ('b', 2) == 'bb'
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
de inut_math importation multiplier def setup_module(module): impression ("module_module d'installation:% s" % module.__prénom__) def teardown_module(module): impression ("module de module de dépot:% s" % module.__prénom__) def setup_function(une fonction): impression ("fonction setup_fonction:% s" % une fonction.__prénom__) def fonction_downdown(une fonction): impression ("fonction teardown_function:% s" % une fonction.__prénom__) def test_numbers_3_4(): impression 'test_numbers_3_4 <============================= Code de test réel' affirmer multiplier(3,4) == 12 def test_strings_a_3(): impression 'test_strings_a_3 <============================= Code de test réel' affirmer multiplier('une',3) == 'aaa' classe TestUM: def installer(soi): impression ("classe d'installation: TestStuff") def abattre(soi): impression ("classe de démontage: TestStuff") def setup_class(cls): impression ("classe setup_class:% s" % cls.__prénom__) def teardown_class(cls): impression ("classe teardown_class:% s" % cls.__prénom__) def setup_method(soi, méthode): impression ("méthode setup_method:% s" % méthode.__prénom__) def teardown_method(soi, méthode): impression ("méthode de teardown_method:% s" % méthode.__prénom__) def test_numbers_5_6(soi): impression 'test_numbers_5_6 <============================= Code de test réel' affirmer multiplier(5,6) == 30 def test_strings_b_2(soi): impression 'test_strings_b_2 <============================= Code de test réel' affirmer multiplier('b',2) == 'bb' |
Pour le voir en action, je vais utiliser le -s option, qui désactive la capture de sortie.
Cela montrera l'ordre des différents appels de fixture.
> py.test -s test_um_pytest_fixtures.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4
collecte en cours … collecté 4 articles
test_um_pytest_fixtures.py ….
=========================== 4 passés en 0.07 secondes ================= =========
module_module d'installation: test_um_pytest_fixtures
Fonction de configuration: test_numbers_3_4
test_numbers_3_4 <============================= Code de test réel
teardown_function function: test_numbers_3_4
Fonction de configuration: test_strings_a_3
test_strings_a_3 <============================= Code de test réel
teardown_function function: test_strings_a_3
setup_class class: TestUM
setup_method méthode: test_numbers_5_6
classe d'installation: TestStuff
test_numbers_5_6 <============================= Code de test réel
classe de démontage: TestStuff
teardown_method méthode: test_numbers_5_6
setup_method méthode: test_strings_b_2
classe d'installation: TestStuff
test_strings_b_2 <============================= Code de test réel
classe de démontage: TestStuff
teardown_method méthode: test_strings_b_2
teardown_class class: TestUM
module de teardown_module: test_um_pytest_fixtures
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
> py.tester –s test_um_pytest_fixtures.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 la collecte ... collecté 4 articles test_um_pytest_fixtures.py .... =========================== 4 passé dans 0,07 secondes =========================== setup_module module:test_um_pytest_fixtures setup_function une fonction:test_numbers_3_4 test_numbers_3_4 <============================ réel tester code fonction_downdown une fonction:test_numbers_3_4 setup_function une fonction:test_strings_a_3 test_strings_a_3 <============================ réel tester code fonction_downdown une fonction:test_strings_a_3 setup_class classe:TestUM setup_method méthode:test_numbers_5_6 installer classe:TestStuff test_numbers_5_6 <============================ réel tester code abattre classe:TestStuff teardown_method méthode:test_numbers_5_6 setup_method méthode:test_strings_b_2 installer classe:TestStuff test_strings_b_2 <============================ réel tester code abattre classe:TestStuff teardown_method méthode:test_strings_b_2 teardown_class classe:TestUM teardown_module module:test_um_pytest_fixtures |
Test de markdown.py
Le code de test pour tester markdown.py ressemblera beaucoup à la version la plus simple, mais sans le passe-partout.
J'utilise également un adaptateur API introduit dans un précédent post.
Voici le code pour utiliser pytest pour tester markdown.py:
depuis markdown_adapter import run_markdown
def test_non_marked_lines ():
print ('dans test_non_marked_lines')
assert run_markdown ('cette ligne n'a pas de traitement spécial') ==
'cette ligne n'a pas de traitement spécial
'
def test_em ():
print ('in test_em')
assert run_markdown ('* cela devrait être encapsulé dans les balises em *') ==
'
cela devrait être enveloppé dans les balises em
'
def test_strong ():
print ('in test_strong')
assert run_markdown ('** ceci devrait être entouré de balises fortes **') ==
'
cela devrait être enveloppé dans des étiquettes fortes
'
de markdown_adapter importation run_markdown def test_non_marked_lines(): impression ('dans test_non_marked_lines') affirmer run_markdown("cette ligne n'a pas de traitement spécial") == 'cette ligne n'a pas de traitement spécial ' def test_em(): impression ('in test_em') affirmer run_markdown('* ceci devrait être enveloppé dans les balises em *') == ' cela devrait être enveloppé dans les balises em ' def test_strong(): impression ('dans test_strong') affirmer run_markdown("** ceci devrait être enveloppé dans des étiquettes fortes **") == ' cela devrait être enveloppé dans des étiquettes fortes ' |
Et voici la sortie:
> py.test test_markdown_pytest.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4
collecter … collectionné 3 articles
test_markdown_pytest.py F.F
================================== FAILURES ============== ======================
____________________________ test_non_marked_lines _____________________________
def test_non_marked_lines ():
print ('dans test_non_marked_lines')
> assert run_markdown ('cette ligne n'a pas de traitement spécial') ==
'cette ligne n'a pas de traitement spécial
'
E affirmez 'cette ligne a … une manipulation spéciale' == '
cette ligne … manipulation
'
E – cette ligne n'a pas de traitement spécial
E + cette ligne n'a pas de traitement spécial
E? +++ ++++
test_markdown_pytest.py:14: AssertionError
——————————- Capturé stdout —————– —————
dans test_non_marked_lines
_________________________________ test_strong __________________________________
def test_strong ():
print ('in test_strong')
> assert run_markdown ('** ceci devrait être entouré de balises fortes **') ==
'
cela devrait être enveloppé dans des étiquettes fortes
'
E affirme '** this shoul … strong tags **' == '
e …
'
E – ** ceci devrait être enveloppé dans des étiquettes fortes **
E + cela devrait être enveloppé dans des étiquettes fortes
test_markdown_pytest.py:24: AssertionError
——————————- Capturé stdout —————– —————
dans test_strong
====================== 2 ont échoué, 1 a été dépassé en 0,30 seconde =================== ==
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
> py.tester test_markdown_pytest.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 la collecte ... collecté 3 articles test_markdown_pytest.py F.F =================================== LES ÉCHECS =================================== ____________________________ test_non_marked_lines _____________________________ def test_non_marked_lines(): impression ('dans test_non_marked_lines') > affirmer run_markdown("cette ligne n'a pas de traitement spécial") == 'cette ligne n'a pas de traitement spécial ' E affirmer "cette ligne a … une manipulation spéciale" == ' cette ligne … manipulation ' E – ce ligne a non spécial manipulation E + ce ligne a non spécial manipulation E ? +++ ++++ test_markdown_pytest.py:14: AssertionError –––––––––––––––– Capturé stdout –––––––––––––––– dans test_non_marked_lines _________________________________ test_strong __________________________________ def test_strong(): impression ('dans test_strong') > affirmer run_markdown("** ceci devrait être enveloppé dans des étiquettes fortes **") == ' cela devrait être enveloppé dans des étiquettes fortes ' E affirmer '** ça devrait … des balises fortes **' == ' e … ' E – **ce devrait être enveloppé dans fort Mots clés** E + <fort>ce devrait être enveloppé dans fort Mots clés</fort> test_markdown_pytest.py:24: AssertionError –––––––––––––––– Capturé stdout –––––––––––––––– dans test_strong ====================== 2 échoué, 1 passé dans 0,30 secondes ====================== |
Vous remarquerez qu'ils échouent tous. C’est exprès, car je n’ai encore implémenté aucun code de démarcation réel.
Cependant, le formatage de la sortie est assez agréable.
C’est assez facile de voir pourquoi le test échoue.
Test de découverte
Le module unittest est livré avec une option "découverte".
Discovery est juste construit pour pytest.
La découverte de test a été utilisée dans mes exemples pour rechercher des tests dans un module spécifié.
Cependant, pytest peut trouver des tests résidant dans plusieurs modules et packages, et même trouver des tests et des doctests.
Pour être honnête, je n’ai pas mémorisé les règles de découverte.
J'essaie juste de faire cela, et à semble fonctionner correctement:
- Nommez mes modules / fichiers de test en commençant par «test_».
- Nommez mes fonctions de test en commençant par «test_».
- Nommez mes classes de test en commençant par 'Test'.
- Nommez mes méthodes de test en commençant par 'test_'.
- Assurez-vous que tous les paquets avec le code de test ont uninitfichier .py ’.
Si je fais tout cela, pytest semble bien trouver tout mon code.
Si vous faites autre chose et que vous ne parvenez pas à obtenir le code de test pytest,
jetez ensuite un coup d’œil à la documentation de découverte de pytest.
Exécuter unittests de Pytest
Pour montrer comment pytest gère les unittests, voici un exemple de test sur les simples tests que j’ai écrits dans son introduction:
> py.test test_um_unittest.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4
collectionner … collectionné 2 articles
test_um_unittest.py ..
=========================== 2 passés en 0.07 secondes ================== =========
> py.test -v test_um_unittest.py
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4 – C: python27 python.exe
collectionner … collectionné 2 articles
test_um_unittest.py:15: TestUM.test_numbers_3_4 PASSED
test_um_unittest.py:18: TestUM.test_strings_a_3 PASSED
=========================== 2 passés en 0.06 secondes ================= =========
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 |
> py.tester test_um_unittest.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 la collecte ... collecté 2 articles test_um_unittest.py .. =========================== 2 passé dans 0,07 secondes =========================== > py.tester –v test_um_unittest.py ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 – C:python27python.EXE la collecte ... collecté 2 articles test_um_unittest.py:15: TestUM.test_numbers_3_4 PASSÉ test_um_unittest.py:18: TestUM.test_strings_a_3 PASSÉ =========================== 2 passé dans 0,06 secondes =========================== |
Comme vous pouvez le constater, je n’ai fourni aucune option supplémentaire, pytest détecte automatiquement l’analyse des désistements.
Exécuter des doctests de Pytest
Selon la documentation, vous pouvez exécuter certains doctests à partir de pytest.
Cependant, avec mes exemples de mise en place de doctests dans des fichiers texte, je ne peux pas trouver un moyen de faire fonctionner pytest.
J’ai essayé plusieurs fois et je continue à avoir des problèmes d’importation:
> py.test –doctest-modules test_unnecessary_math.txt
============================== La session de test commence ================= ============
plate-forme win32 – Python 2.7.3 – pytest-2.2.4
collecter … collectionné 1 articles
test_unnecessary_math.txt F
================================== FAILURES ============== ======================
__________________________________ [doctest] ___________________________________
001 Ceci est une suite de régression basée sur un test de documentation pour inutile_math.py
002 Chaque ligne '>>>' est exécutée comme dans un shell python et compte comme un test.
003 La ligne suivante, sinon «>>>», est la sortie attendue de la ligne précédente.
004 Si quelque chose ne correspond pas exactement (y compris les espaces finaux), le test échoue.
005
006 >>> à partir de require_math import multiplier
UNEXPECTED EXCEPTION: ImportError ('Aucun module nommé inutile_math',)
Traceback (dernier appel le plus récent):
Fichier "C: python27 lib doctest.py", ligne 1289, dans __run
compileflags, 1) dans test.globs
Fichier "
ImportError: Aucun module nommé inutile_math
E: python_notes repo markdown.py-dev exemple_simple test_unnecessary_math.txt: 6: UnexpectedException
=========================== 1 a échoué en 0.06 secondes ================= =========
1 2 3 4 5 6 7 8 9 dix 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
> py.tester –doctest–modules test_unnecessary_math.SMS ============================= tester session départs ============================== Plate-forme win32 – Python 2.7.3 – pytest–2.2.4 la collecte ... collecté 1 articles test_unnecessary_math.SMS F =================================== LES ÉCHECS =================================== __________________________________ [[[[doctest] ___________________________________ 001 Ce est une doctest basé régression suite pour inut_math.py 002 Chaque '>>>' ligne est courir comme si dans une python coquille, et compte comme une tester. 003 le suivant ligne, si ne pas '>>>' est le attendu sortie de le précédent ligne. 004 Si n'importe quoi nene correspond pas exactement (y compris les espaces de fin), le test échoue. 005 006 >>> à partir de require_math import multiplier EXCEPTION INATTENDUE: ImportError ('Non module nommé inutile_math',) Traceback (plus récent appel dernier): Fichier "C: python27 lib doctest.py", ligne 1289, dans __courir drapeaux de compilation, 1) dans tester.globs Fichier " ImportError: Non module nommé inutile_math E:python_notesreporéduction.py–devexemple simpletest_unnecessary_math.SMS:6: Exception inattendue =========================== 1 échoué dans 0,06 secondes =========================== |
Si quelqu'un sait ce que je ne fais pas bien, faites-le-moi savoir.
Merci d'avance.
Plus d'infos sur pytest (liens)
Exemples sur github
Tous les exemples ici sont disponibles dans le projet markdown.py sur github.
Prochain
Dans le prochain post, je vais mettre le nez au problème de l’espace.
[ad_2]