Test Python avec pytest. Premiers pas avec pytest, Chapitre 1

Retour Suivant


J'ai trouvĂ© que le test Python avec pytest est un guide d'introduction extrĂȘmement utile Ă  l'environnement de test pytest. Cela me rapporte dĂ©jĂ  des dividendes dans mon entreprise.

Rasoir Chris
Vice-président Produit, Uprising Technology



Les exemples de ce livre sont écrits en utilisant Python 3.6 et pytest 3.2. pytest 3.2 prend en charge Python 2.6, 2.7 et Python 3.3+.


Le code source du projet Tùches, ainsi que pour tous les tests présentés dans ce livre, est disponible sur le lien sur la page Web du livre à pragprog.com . Vous n'avez pas besoin de télécharger le code source pour comprendre le code de test; le code de test est présenté sous une forme pratique dans les exemples. Mais pour suivre les tùches du projet ou adapter des exemples de test pour tester votre propre projet (vos mains ne sont pas liées!), Vous devez vous rendre sur la page Web du livre et télécharger le travail. Là, sur la page Web du livre, il y a un lien pour les messages d' erreur et un forum de discussion .

Sous le spoiler se trouve une liste d'articles de cette série.



On y va


Voici le test:


ch1 / test_one.py

def test_passing(): assert (1, 2, 3) == (1, 2, 3) 

Voici à quoi cela ressemble au démarrage:


 $ cd /path/to/code/ch1 $ pytest test_one.py ===================== test session starts ====================== collected 1 items test_one.py . =================== 1 passed in 0.01 seconds =================== 


Le point aprÚs test_one.py signifie qu'un test a été exécuté et qu'il a réussi. Si vous avez besoin de plus d'informations, vous pouvez utiliser -v ou --verbose :


 $ pytest -v test_one.py ===================== test session starts ====================== collected 1 items test_one.py::test_passing PASSED =================== 1 passed in 0.01 seconds =================== 


Si vous avez un terminal couleur, PASSÉ et la ligne du bas sont vertes. Super!


Il s'agit d'un test qui a échoué:


ch1 / test_two.py

 def test_failing(): assert (1, 2, 3) == (3, 2, 1) 

La façon dont pytest montre les échecs de test est l'une des nombreuses raisons pour lesquelles les développeurs aiment pytest. Voyons ce qui en résulte:


 $ pytest test_two.py ===================== test session starts ====================== collected 1 items test_two.py F =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =================== 1 failed in 0.04 seconds =================== 


Super! Le test test test_failing obtient sa section pour nous montrer pourquoi il a échoué.


Et pytest signale avec précision que le premier crash: l'index 0 est un décalage.


Une grande partie de ce message est rouge, ce qui le fait vraiment ressortir (si vous avez un terminal couleur).


C'est déjà beaucoup d'informations, mais il y a une ligne avec un indice qui dit Utilisez -v pour obtenir encore plus de descriptions des différences.


-v celui -v :


 $ pytest -v test_two.py ===================== test session starts ====================== collected 1 items test_two.py::test_failing FAILED =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Full diff: E - (1, 2, 3) E ? ^ ^ E + (3, 2, 1) E ? ^ ^ test_two.py:2: AssertionError =================== 1 failed in 0.04 seconds =================== 


Ouah!
pytest ajoute le caractÚre caret (^) pour nous montrer exactement quelle est la différence.
Si vous ĂȘtes dĂ©jĂ  impressionnĂ© par la facilitĂ© d'Ă©criture, de lecture et d'exĂ©cution des tests avec pytest et par la facilitĂ© de lecture de la sortie pour voir oĂč la panne s'est produite, eh bien ... vous n'avez encore rien vu. D'oĂč cela vient, il y a beaucoup plus de miracles. Restez et laissez-moi vous montrer pourquoi je pense que pytest est absolument la meilleure plate-forme de test.


Dans la suite de ce chapitre, vous allez installer pytest, regarder les différentes façons de l'exécuter et suivre certaines des options de ligne de commande les plus utilisées. Dans les prochains chapitres, vous apprendrez comment écrire des fonctions de test qui maximisent la puissance de pytest, comment obtenir le code d'installation dans les sections de configuration et de démontage appelées fixtures, et comment utiliser des fixtures et des plugins pour surcharger vraiment les tests de logiciels.


Mais je dois d'abord m'excuser. DĂ©solĂ© de tester assert (1, 2, 3) == (3, 2, 1) , c'est tellement ennuyeux. J'entends des ronflements?! Personne n'Ă©crirait un tel test dans la vraie vie. Les tests logiciels consistent en du code qui vĂ©rifie d'autres logiciels qui, hĂ©las, ne fonctionneront pas toujours de maniĂšre positive. Et (1, 2, 3) == (1, 2, 3) fonctionnera toujours. C'est pourquoi nous n'utiliserons pas de tests trop stupides comme celui-ci dans le reste du livre. Nous envisagerons des tests pour un vrai projet logiciel. Nous utiliserons un exemple de projet TĂąches qui nĂ©cessite un code de test. J'espĂšre que c'est assez simple pour ĂȘtre facilement compris, mais pas assez simple pour ĂȘtre ennuyeux.


Une autre application utile des tests de logiciels consiste Ă  tester vos hypothĂšses sur le fonctionnement du logiciel testĂ©, ce qui peut inclure le test de votre comprĂ©hension des modules et packages tiers, et mĂȘme la crĂ©ation de structures de donnĂ©es Python.


Le projet Tùches utilise la structure Tùche, basée sur la méthode d'usine namedtuple, qui fait partie de la bibliothÚque standard. La structure de tùches est utilisée comme structure de données pour transférer des informations entre l'interface utilisateur et l'API.


Dans le reste de ce chapitre, j'utiliserai Task pour montrer comment démarrer pytest et utiliser certaines options de ligne de commande couramment utilisées.


Voici la tĂąche:


 from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) 

Avant de passer aux exemples, prenons un peu de recul et parlons de la façon d'obtenir Pytest et de l'installer.


La fonction factory namedtuple () existe depuis Python 2.6, mais je trouve toujours que de nombreux développeurs Python ne savent pas à quel point c'est cool. Au moins, l'utilisation de la tùche pour les cas de test sera plus intéressante que (1, 2, 3) == (1, 2, 3) ou (1, 2)==3 .


Avant de passer aux exemples, prenons un peu de recul et parlons oĂč trouver pytest et comment l'installer.


Pytest minier


SiĂšge social de Pytest https://docs.pytest.org . Ceci est la documentation officielle. Mais il se propage via PyPI (l'index du package Python) Ă  https://pypi.python.org/pypi/pytest .


Comme les autres packages Python distribués via PyPI, utilisez pip pour installer pytest dans l'environnement virtuel utilisé pour les tests:


 $ pip3 install -U virtualenv $ python3 -m virtualenv venv $ source venv/bin/activate $ pip install pytest 

Si vous n'ĂȘtes pas familier avec virtualenv ou pip, je vais vous prĂ©senter. Voir Annexe 1, «Environnements virtuels», page 155 et Annexe 2, page 159.


Qu'en est-il de Windows, Python 2 et Venv?


L'exemple virtualenv et pip devrait fonctionner sur de nombreux systÚmes POSIX, tels que Linux et macOS, ainsi que sur de nombreuses versions de Python, y compris Python 2.7.9 et versions ultérieures.


La source venv / bin / activate dans la ligne ne fonctionnera pas pour Windows, utilisez plutĂŽt venv \ Scripts \ activate.bat .
Faites ceci:


 C:\> pip3 install -U virtualenv C:\> python3 -m virtualenv venv C:\> venv\Scripts\activate.bat (venv) C:\> pip install pytest 

Pour Python 3.6 et supérieur, vous pouvez vous débrouiller avec venv au lieu de virtualenv, et vous n'avez pas à vous soucier de l'installer en premier. Il est inclus dans Python 3.6 et versions ultérieures. Cependant, j'ai entendu dire que certaines plates-formes se comportent toujours mieux avec virtualenv.


Exécuter pytest


 $ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ... 

En l'absence d'arguments, pytest examinera votre répertoire actuel et tous les sous-répertoires des fichiers de test et exécutera le code de test qu'il trouve. Si vous donnez à pytest un nom de fichier, un nom de répertoire ou une liste d'entre eux, ils y seront trouvés à la place du répertoire actuel. Chaque répertoire spécifié sur la ligne de commande est récursivement examiné pour trouver le code de test.


Par exemple, créons un sous-répertoire appelé tùches et commençons par ce fichier de test:


ch1 / tasks / test_three.py

 """   Task.""" from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) Task.__new__.__defaults__ = (None, None, False, None) def test_defaults(): """  ,      .""" t1 = Task() t2 = Task(None, None, False, None) assert t1 == t2 def test_member_access(): """  .field () namedtuple.""" t = Task('buy milk', 'brian') assert t.summary == 'buy milk' assert t.owner == 'brian' assert (t.done, t.id) == (False, None) 

Le test test_member_access () consiste à montrer comment accéder aux membres par leur nom et non par leur index, ce qui est l'une des principales raisons d'utiliser des nommés.


Mettons quelques tests supplémentaires dans un deuxiÚme fichier pour montrer les fonctionnalités _asdict () et _replace ()


Vous pouvez utiliser __new __.__ defaults__ pour créer des objets Task sans spécifier tous les champs. Le test test_defaults () est destiné à démontrer et à tester le fonctionnement des valeurs par défaut.


Le test test_member_access() doit montrer comment accéder aux membres nommés nd et non par index, ce qui est l'une des principales raisons d'utiliser des nommés.
Ajoutons quelques tests supplémentaires au deuxiÚme fichier pour illustrer les fonctions _asdict() et _replace()


ch1 / tasks / test_four.py

 """   Task.""" from collections import namedtuple Task = namedtuple('Task', ['summary', 'owner', 'done', 'id']) Task.__new__.__defaults__ = (None, None, False, None) def test_asdict(): """_asdict()   .""" t_task = Task('do something', 'okken', True, 21) t_dict = t_task._asdict() expected = {'summary': 'do something', 'owner': 'okken', 'done': True, 'id': 21} assert t_dict == expected def test_replace(): """    fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 10) assert t_after == t_expected 

Pour exécuter pytest, vous avez la possibilité de spécifier des fichiers et des répertoires. Si vous ne spécifiez aucun fichier ou répertoire, pytest recherchera les tests dans le répertoire de travail et les sous-répertoires actuels. Il recherche les fichiers commençant par test_ ou se terminant par _test. Si vous exécutez pytest à partir du répertoire ch1, sans commandes, vous exécuterez des tests pour quatre fichiers:


 $ cd /path/to/code/ch1 $ pytest ===================== test session starts ====================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========================== FAILURES =========================== _________________________ test_failing _________________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError ============== 1 failed, 5 passed in 0.08 seconds ============== 


Pour exĂ©cuter uniquement nos nouveaux tests de tĂąches, vous pouvez fournir Ă  pytest tous les noms de fichiers que vous souhaitez exĂ©cuter, ou un rĂ©pertoire, ou appeler pytest Ă  partir du rĂ©pertoire oĂč se trouvent nos tests:


 $ pytest tasks/test_three.py tasks/test_four.py ===================== test session starts ====================== collected 4 items tasks/test_three.py .. tasks/test_four.py .. =================== 4 passed in 0.02 seconds =================== $ pytest tasks ===================== test session starts ====================== collected 4 items tasks/test_four.py .. tasks/test_three.py .. =================== 4 passed in 0.03 seconds =================== $ cd /path/to/code/ch1/tasks $ pytest ===================== test session starts ====================== collected 4 items test_four.py .. test_three.py .. =================== 4 passed in 0.02 seconds =================== 


La partie d'exĂ©cution de pytest, oĂč pytest rĂ©ussit et trouve les tests Ă  exĂ©cuter, est appelĂ©e dĂ©couverte de test. pytest a pu trouver tous les tests que nous voulions exĂ©cuter car nous les avons nommĂ©s selon les conventions de dĂ©nomination de pytest.


Voici un bref aperçu des conventions de dĂ©nomination afin que votre code de test puisse ĂȘtre dĂ©tectĂ© Ă  l'aide de pytest:


  • Les fichiers de test doivent ĂȘtre nommĂ©s test_<something>.py ou <something>_test.py .
  • Les mĂ©thodes et fonctions de test doivent ĂȘtre appelĂ©es test_<something> .
  • Les classes de test doivent ĂȘtre appelĂ©es Test<Something> .

Puisque nos fichiers et fonctions de test commencent par test_ , tout va bien pour nous. Il existe des moyens de modifier ces rÚgles de découverte si vous disposez de plusieurs tests avec des noms différents.
Je couvrirai cela au chapitre 6, «Configuration», page 113.


Examinons de plus prÚs le résultat de l'exécution d'un seul fichier:


 $ cd /path/to/code/ch1/tasks $ pytest test_three.py ================= test session starts ================== platform darwin -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0 rootdir: /path/to/code/ch1/tasks, inifile: collected 2 items test_three.py .. =============== 2 passed in 0.01 seconds =============== 

Le résultat nous en dit long.


===== la session de test démarre ====

.


pytest fournit un séparateur élégant pour démarrer une session de test. Une session est un appel pytest unique, comprenant tous les tests exécutés dans plusieurs répertoires. Cette définition de session devient importante lorsque je parle de la zone de session par rapport aux appareils Pytest dans la définition de la région de l'appareil, à la page 56.


Plateforme Darwin - sur mon Mac. Sur un ordinateur Windows, la plateforme est diffĂ©rente. Les versions suivantes sont des versions de Python et pytest, ainsi que des dĂ©pendances sur les packages pytest. Py et pluggy sont des packages dĂ©veloppĂ©s par l'Ă©quipe pytest pour aider Ă  la mise en Ɠuvre de pytest.


rootdir: / chemin / vers / code / ch1 / tĂąches, inifile:

rootdir est le rĂ©pertoire partagĂ© le plus haut pour tous les rĂ©pertoires dans lesquels le code de test est recherchĂ©. Le inifile (vide ici) rĂ©pertorie les fichiers de configuration utilisĂ©s. Les fichiers de configuration peuvent ĂȘtre pytest.ini , tox.ini ou setup.cfg . Pour plus d'informations sur les fichiers de configuration, reportez-vous au chapitre 6, «Configuration», page 113.


collecté 2 articles

Ce sont deux fonctions de test dans un fichier.


test_three.py ..

test_three.py montre le fichier de test. Il y a une ligne pour chaque fichier de test. Deux points signifient que les tests sont réussis - un point pour chaque fonction ou méthode de test. Les points sont destinés uniquement à la réussite des tests. Les échecs, les erreurs, les sauts, les xfails et les xpasses sont notés respectivement F, E, s, x et X. Si vous voulez voir plus de points pour réussir les tests, utilisez l'option -v ou --verbose .


== 2 passés en 0,01 secondes ==

Cette ligne fait référence au nombre de tests réussis et au temps passé sur toute la session de test. S'il y a des tests réussis, le numéro de chaque catégorie sera également affiché ici.


Un résultat de test est le principal moyen par lequel un utilisateur effectuant un test ou affichant les résultats peut comprendre ce qui s'est passé pendant le test. Dans pytest, les fonctions de test peuvent avoir plusieurs résultats différents, pas seulement réussir ou échouer. Voici les résultats possibles de la fonction de test:


  • RÉUSSI (.): Le test s'est terminĂ© avec succĂšs.
  • FAILED (F): Le test a Ă©chouĂ© (ou XPASS + strict).
  • SKIPPED (s): Le test a Ă©tĂ© ignorĂ©. Vous pouvez forcer pytest Ă  ignorer le test Ă  l'aide des @pytest.mark.skip() ou pytest.mark.skipif() , dĂ©crits dans la section sur les tests Ă  sauter, Ă  la page 34.
  • xfail (x): Le test n'aurait pas dĂ» rĂ©ussir, il a Ă©tĂ© lancĂ© et a Ă©chouĂ©. Vous pouvez forcer pytest Ă  indiquer que le test doit Ă©chouer Ă  l'aide du @pytest.mark.xfail() , dĂ©crit dans les marquages ​​des tests comme prĂ©voyant un Ă©chec, Ă  la page 37.
  • XPASS (X): Le test n'aurait pas dĂ» passer, il a Ă©tĂ© lancĂ© et rĂ©ussi! ..
  • ERREUR (E): Une exception s'est produite en dehors de la fonction de test, soit dans le luminaire, abordĂ© au chapitre 3, Appareils pytest, Ă  la page 49, ou dans la fonction de crochet, discutĂ© au chapitre 5, Plugins, Ă  la page 95.

Exécution d'un seul test


Peut-ĂȘtre que la premiĂšre chose Ă  faire aprĂšs avoir commencĂ© Ă  Ă©crire des tests est d'en exĂ©cuter un seul. SpĂ©cifiez le fichier directement et ajoutez le nom ::test_name :


 $ cd /path/to/code/ch1 $ pytest -v tasks/test_four.py::test_asdict =================== test session starts =================== collected 3 items tasks/test_four.py::test_asdict PASSED ================ 1 passed in 0.01 seconds ================= 

Voyons maintenant quelques options.


Utilisation des options


Nous avons utilisĂ© l'option verbose, -v ou --verbose plusieurs fois, mais il existe de nombreuses autres options qui mĂ©ritent d'ĂȘtre connues. Nous n'allons pas tous les utiliser dans ce livre, seulement quelques-uns. Vous pouvez afficher la liste complĂšte Ă  l'aide de l'option pytest --help .


Vous trouverez ci-dessous quelques options trĂšs utiles lorsque vous travaillez avec pytest. Ce n'est pas une liste complĂšte, mais ces options sont suffisantes pour commencer.


 $ pytest --help usage: pytest [options] [file_or_dir] [file_or_dir] [...] ... subset of the list ... positional arguments: file_or_dir general: -k EXPRESSION only run tests which match the given substring expression. An expression is a python evaluatable expression where all names are substring-matched against test names and their parent classes. Example: -k 'test_method or test_other' matches all test functions and classes whose name contains 'test_method' or 'test_other', while -k 'not test_method' matches those that don't contain 'test_method' in their names. Additionally keywords are matched to classes and functions containing extra names in their 'extra_keyword_matches' set, as well as functions which have names assigned directly to them. -m MARKEXPR only run tests matching given mark expression. example: -m 'mark1 and not mark2'. --markers show markers (builtin, plugin and per-project ones). -x, --exitfirst exit instantly on first error or failed test. --maxfail=num exit after first num failures or errors. ... --capture=method per-test capturing method: one of fd|sys|no. -s shortcut for --capture=no. ... --lf, --last-failed rerun only the tests that failed at the last run (or all if none failed) --ff, --failed-first run all tests but run the last failures first. This may re-order tests and thus lead to repeated fixture setup/teardown ... reporting: -v, --verbose increase verbosity. -q, --quiet decrease verbosity. --verbosity=VERBOSE set verbosity ... -l, --showlocals show locals in tracebacks (disabled by default). --tb=style traceback print mode (auto/long/short/line/native/no). ... --durations=N show N slowest setup/test durations (N=0 for all). ... collection: --collect-only only collect tests, don't execute them. ... test session debugging and configuration: --basetemp=dir base temporary directory for this test run.(warning: this directory is removed if it exists) --version display pytest lib version and import information. -h, --help show help message and configuration info 

--collect seulement


L' --collect-only indique quels tests seront effectuĂ©s avec les paramĂštres et la configuration donnĂ©s. Il est pratique d'afficher ce paramĂštre en premier, afin que la sortie puisse ĂȘtre utilisĂ©e comme rĂ©fĂ©rence pour d'autres exemples. Si vous dĂ©marrez dans le rĂ©pertoire ch1, vous devriez voir toutes les fonctions de test que vous avez examinĂ©es jusqu'Ă  prĂ©sent dans ce chapitre:


 $ cd /path/to/code/ch1 $ pytest --collect-only =================== test session starts =================== collected 6 items <Module 'test_one.py'> <Function 'test_passing'> <Module 'test_two.py'> <Function 'test_failing'> <Module 'tasks/test_four.py'> <Function 'test_asdict'> <Function 'test_replace'> <Module 'tasks/test_three.py'> <Function 'test_defaults'> <Function 'test_member_access'> ============== no tests ran in 0.03 seconds =============== 

L' --collect-only est utile pour vérifier que les autres options qui sélectionnent les tests avant d'exécuter les tests sont sélectionnées correctement. Nous l'utiliserons à nouveau avec -k pour montrer comment cela fonctionne.


-k EXPRESSION


L' -k vous permet d'utiliser une expression pour définir des fonctions de test.


Une option trĂšs puissante! Il peut ĂȘtre utilisĂ© comme raccourci pour exĂ©cuter un test distinct, si le nom est unique, ou exĂ©cuter un ensemble de tests qui ont un prĂ©fixe ou un suffixe commun dans leurs noms. Supposons que vous souhaitiez exĂ©cuter les tests test_asdict() et test_defaults() . Vous pouvez vĂ©rifier le filtre avec: --collect-only :


 $ cd /path/to/code/ch1 $ pytest -k "asdict or defaults" --collect-only =================== test session starts =================== collected 6 items <Module 'tasks/test_four.py'> <Function 'test_asdict'> <Module 'tasks/test_three.py'> <Function 'test_defaults'> =================== 4 tests deselected ==================== ============== 4 deselected in 0.03 seconds =============== 

Ouais! C'est semblable à ce dont nous avons besoin. Vous pouvez maintenant les exécuter en supprimant --collect-only :


 $ pytest -k "asdict or defaults" =================== test session starts =================== collected 6 items tasks/test_four.py . tasks/test_three.py . =================== 4 tests deselected ==================== ========= 2 passed, 4 deselected in 0.03 seconds ========== 

Oups! Juste des points. Alors, ils sont passés. Mais étaient-ce les bons tests? Une façon de le savoir est d'utiliser -v ou --verbose :


 $ pytest -v -k "asdict or defaults" =================== test session starts =================== collected 6 items tasks/test_four.py::test_asdict PASSED tasks/test_three.py::test_defaults PASSED =================== 4 tests deselected ==================== ========= 2 passed, 4 deselected in 0.02 seconds ========== 

Ouais! Ce sont les bons tests.


-m MARKEXPR


Les marqueurs sont l'un des meilleurs moyens de baliser un sous-ensemble de fonctionnalitĂ©s de test Ă  exĂ©cuter ensemble. Par exemple, une façon d'exĂ©cuter test_replace() et test_member_access() , mĂȘme s'ils sont dans des fichiers sĂ©parĂ©s, est de les marquer. Tout nom de marqueur peut ĂȘtre utilisĂ©. run_these_please que vous souhaitez utiliser run_these_please . Notez les tests utilisant le dĂ©corateur @pytest.mark.run_these_please , comme ceci:


 import pytest ... @pytest.mark.run_these_please def test_member_access(): ... 

Maintenant la mĂȘme chose pour test_replace() . Ensuite, vous pouvez exĂ©cuter tous les tests avec le mĂȘme marqueur avec pytest -m run_these_please :


 $ cd /path/to/code/ch1/tasks $ pytest -v -m run_these_please ================== test session starts =================== collected 4 items test_four.py::test_replace PASSED test_three.py::test_member_access PASSED =================== 2 tests deselected =================== ========= 2 passed, 2 deselected in 0.02 seconds ========= 

L'expression du marqueur ne doit pas nĂ©cessairement ĂȘtre un marqueur unique. Vous pouvez utiliser des options telles que -m "mark1 and mark2" pour les tests avec les deux marqueurs, -m "mark1 and not mark2" pour les tests qui ont le label 1 mais pas le label 2, -m "mark1 or mark2" pour les tests avec l'un des, etc., j'examinerai plus en dĂ©tail les marqueurs dans les mĂ©thodes de vĂ©rification du marquage, Ă  la page 31.


-x, --exitfirst


Le comportement normal de pytest est d'exĂ©cuter tous les tests qu'il trouve. Si la fonction de test dĂ©tecte un Ă©chec d' assertion ou d' exception , ce test s'arrĂȘte et le test Ă©choue. Et puis pytest exĂ©cute le prochain test. Pour la plupart, c'est ce dont vous avez besoin. Cependant, en particulier lors du dĂ©bogage d'un problĂšme, il interfĂšre immĂ©diatement avec la session de test entiĂšre lorsque le test n'est pas correct. C'est ce que fait l'option -x . Essayons ceci sur les six tests que nous avons en ce moment:


 $ cd /path/to/code/ch1 $ pytest -x ====================== test session starts ==================== collected 6 items test_one.py . test_two.py F ============================ FAILURES ========================= __________________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =============== 1 failed, 1 passed in 0.38 seconds ============ 

En haut de la sortie, vous voyez que les six tests (ou «élĂ©ments») ont Ă©tĂ© collectĂ©s, et sur la ligne du bas, vous voyez qu'un test a Ă©chouĂ© et un rĂ©ussi, et pytest a affichĂ© la ligne «Interrompu» pour nous faire savoir qu'il est arrĂȘtĂ©. Sans -x les six tests s'exĂ©cuteraient. RĂ©pĂ©tons encore sans -x . Nous utilisons Ă©galement --tb=no pour dĂ©sactiver le traçage de pile, car vous l'avez dĂ©jĂ  vu et vous n'avez pas besoin de le revoir:


 $ cd /path/to/code/ch1 $ pytest --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============ 

, -x , pytest test_two.py .


--maxfail=num


-x . , , , --maxfail , , . , . , --maxfail = 2 , , --maxfail = 1 , -x :


 $ cd /path/to/code/ch1 $ pytest --maxfail=2 --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.08 seconds ============ $ pytest --maxfail=1 --tb=no =================== test session starts =================== collected 6 items test_one.py . test_two.py F !!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!! =========== 1 failed, 1 passed in 0.19 seconds ============ 

E --tb=no , .


-s and --capture=method


-s — , stdout , . --capture=no . , . , , , . -s --capture=no . print() , .


, , -l/--showlocals , , .


--capture=fd --capture=sys . — --capture=sys sys.stdout/stderr mem-. --capture=fd 1 2 .


sys fd . , , . -s . , -s , .


; . , , .


-lf, --last-failed


. --lf :


, --tb , , .


 $ cd /path/to/code/ch1 $ pytest --lf =================== test session starts =================== run-last-failure: rerun last 1 failures collected 6 items test_two.py F ======================== FAILURES ========================= ______________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Use -v to get the full diff test_two.py:2: AssertionError =================== 5 tests deselected ==================== ========= 1 failed, 5 deselected in 0.08 seconds ========== 

–ff, --failed-first


--ff/--failed-first , --last-failed , , :


 $ cd /path/to/code/ch1 $ pytest --ff --tb=no =================== test session starts =================== run-last-failure: rerun last 1 failures first collected 6 items test_two.py F test_one.py . tasks/test_four.py .. tasks/test_three.py .. =========== 1 failed, 5 passed in 0.09 seconds ============ 

test_failing() test\_two.py test\_one.py . , test_failing() , --ff


-v, --verbose


-v/--verbose . , , .


, --ff --tb=no :


 $ cd /path/to/code/ch1 $ pytest -v --ff --tb=no =================== test session starts =================== run-last-failure: rerun last 1 failures first collected 6 items test_two.py::test_failing FAILED test_one.py::test_passing PASSED tasks/test_four.py::test_asdict PASSED tasks/test_four.py::test_replace PASSED tasks/test_three.py::test_defaults PASSED tasks/test_three.py::test_member_access PASSED =========== 1 failed, 5 passed in 0.07 seconds ============ 


FAILED PASSED.


-q, --quiet


-q/--quiet -v/--verbose ; . --tb=line , .


- q :


 $ cd /path/to/code/ch1 $ pytest -q .F.... ======================== FAILURES ========================= ______________________ test_failing _______________________ def test_failing(): > assert (1, 2, 3) == (3, 2, 1) E assert (1, 2, 3) == (3, 2, 1) E At index 0 diff: 1 != 3 E Full diff: E - (1, 2, 3) E ? ^ ^ E + (3, 2, 1) E ? ^ ^ test_two.py:2: AssertionError 1 failed, 5 passed in 0.08 seconds 

-q , . -q ( --tb=no ), , .


-l, --showlocals


-l/--showlocals tracebacks .


. test_replace()


 t_expected = Task('finish book', 'brian', True, 10) 

sur


 t_expected = Task('finish book', 'brian', True, 11) 

10 11 . . --l/--showlocals :


 $ cd /path/to/code/ch1 $ pytest -l tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= ______________________ test_replace _______________________ @pytest.mark.run_these_please def test_replace(): """replace() should change passed in fields.""" t_before = Task('finish book', 'brian', False) t_after = t_before._replace(id=10, done=True) t_expected = Task('finish book', 'brian', True, 11) > assert t_after == t_expected E AssertionError: assert Task(summary=...e=True, id=10) == Task(summary='...e=True, id=11) E At index 3 diff: 10 != 11 E Use -v to get the full diff t_after = Task(summary='finish book', owner='brian', done=True, id=10) t_before = Task(summary='finish book', owner='brian', done=False, id=None) t_expected = Task(summary='finish book', owner='brian', done=True, id=11) tasks\test_four.py:28: AssertionError =========== 1 failed, 3 passed in 0.08 seconds ============ 


t_after , t_before t_expected , assert-.


--tb=style


--tb=style . pytest , , . tracebacks , , . --tb=style . , , short, line no. short assert E ; line ; no .


test_replace() , , . --tb=no


 $ cd /path/to/code/ch1 $ pytest --tb=no tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. =========== 1 failed, 3 passed in 0.04 seconds ============ 

--tb=line in many cases is enough to tell what's wrong. If you have a ton of failing tests, this option can help to show a pattern in the failures:

--tb=line , , . , :


 $ pytest --tb=line tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= /path/to/code/ch1/tasks/test_four.py:20: AssertionError: assert Task(summary=...e=True, id=10) == Task( summary='...e=True, id=11) =========== 1 failed, 3 passed in 0.05 seconds ============ 

verbose tracebacks --tb=short :


 $ pytest --tb=short tasks =================== test session starts =================== collected 4 items tasks/test_four.py .F tasks/test_three.py .. ======================== FAILURES ========================= ______________________ test_replace _______________________ tasks/test_four.py:20: in test_replace assert t_after == t_expected E AssertionError: assert Task(summary=...e=True, id=10) == Task( summary='...e=True, id=11) E At index 3 diff: 10 != 11 E Use -v to get the full diff =========== 1 failed, 3 passed in 0.04 seconds ============ 

, , .


, .


pytest --tb=long traceback. pytest --tb=auto tracebacks , . . pytest --tb=native traceback .


--durations=N


--durations=N , . ; N tests/setups/teardowns . --durations=0 , .


, time.sleep(0.1) . , :


 $ cd /path/to/code/ch1 $ pytest --durations=3 tasks ================= test session starts ================= collected 4 items tasks/test_four.py .. tasks/test_three.py .. ============== slowest 3 test durations =============== 0.10s call tasks/test_four.py::test_replace 0.00s setup tasks/test_three.py::test_defaults 0.00s teardown tasks/test_three.py::test_member_access ============== 4 passed in 0.13 seconds 

sleep , . : call(), (setup) (teardown). , , , , . 3, pytest Fixtures, . 49.


--version


--version pytest , :


 $ pytest --version This is pytest version 3.0.7, imported from /path/to/venv/lib/python3.5/site-packages/pytest.py 

pytest , pytest site-packages .


-h, --help


-h/--help , , pytest. , stock- pytest, , , .


-h :


  • : pytest [] [file_or_dir] [file_or_dir] [...]
  • ,
  • , ini , 6, , . 113
  • , pytest ( 6, , . 113)
  • , pytest --markers , 2, , . 23
  • , pytest --fixtures , 3, pytest, . 49

:


 (shown according to specified file_or_dir or current dir if not specified) 

, , . , pytest conftest.py, - (hook functions), , .


pytest conftest.py . conftest.py ini, pytest.ini 6 «», 113.


Exercices


  1. , python -m virtualenv python -m venv . , , , , , . , . 1, , . 155, .


  2. .


     - $ source venv/bin/activate - $ deactivate On Windows: - C:\Users\okken\sandbox>venv\scripts\activate.bat - C:\Users\okken\sandbox>deactivate 

  3. pytest . . 2, pip, 159, . , pytest, , .


  4. . , . pytest .


  5. assert. assert something == something_else; , :


    • assert 1 in [2, 3, 4]
    • assert a < b
    • assert 'fizz' not in 'fizzbuzz'


Et ensuite


, pytest . , . , , , , .


Retour Suivant

Source: https://habr.com/ru/post/fr448782/


All Articles