Retour Suivant 
Dans ce chapitre, nous examinerons les fichiers de configuration qui affectent pytest, discuterons de la façon dont pytest modifie son comportement en fonction d'eux et apporterons quelques modifications aux tùches de configuration du projet Tùches.

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.
La configuration
Jusqu'à présent, dans ce livre, j'ai parlé de divers fichiers non-test qui affectent pytest principalement en passant, à l'exception de conftest.py, que j'ai traité en détail dans le chapitre 5, Plugins, à la page 95. Dans ce chapitre, nous examinerons les fichiers de configuration, qui affectent pytest, discutez de la maniÚre dont pytest modifie son comportement en fonction de ceux-ci et apportez quelques modifications aux fichiers de configuration du projet Tùches.
Comprendre les fichiers de configuration pytest
Avant de vous dire comment vous pouvez changer le comportement par défaut dans pytest, passons en revue tous les fichiers non test dans pytest et, en particulier, qui devrait s'en occuper.
Vous devez savoir ce qui suit:
- pytest.ini : il s'agit du fichier de configuration Pytest principal qui vous permet de modifier le comportement par dĂ©faut. Ătant donnĂ© que vous pouvez effectuer plusieurs modifications de configuration, la majeure partie de ce chapitre est consacrĂ©e aux paramĂštres que vous pouvez effectuer dans
pytest.ini
. - conftest.py : il s'agit d'un plugin local qui permet aux fonctions de hook et aux appareils d'ĂȘtre connectĂ©s au rĂ©pertoire dans lequel le fichier
conftest.py
et à tous ses sous-répertoires. Le fichier conftest.py
décrit dans le chapitre 5 «Plugins» à la page 95. __init__.py
: Lorsqu'il est placé dans chaque sous-répertoire de test, ce fichier vous permet d'avoir des noms de fichier de test identiques dans plusieurs répertoires de test. Nous allons voir un exemple de ce qui pourrait mal se passer sans les fichiers __init__.py
dans les rĂ©pertoires de test dans l'article «Ăviter les collisions de noms de fichiers» Ă la page 120.
Si vous utilisez tox, vous serez intéressé par:
- tox.ini : Ce fichier est similaire Ă
pytest.ini
, mais pour tox
. Cependant, vous pouvez placer votre configuration pytest
au lieu d'avoir Ă la fois le fichier tox.ini
et le fichier pytest.ini
, vous économisant un fichier de configuration. Tox est abordé au chapitre 7, «Utilisation de pytest avec d'autres outils», page 125.
Si vous souhaitez distribuer un package Python (par exemple Tùches), ce fichier sera intéressant:
- setup.cfg : il s'agit également d'un fichier INI qui affecte le comportement de
setup.py
. Vous pouvez ajouter quelques lignes Ă setup.py
pour exécuter le python setup.py test
et exĂ©cuter tous vos tests pytest. Si vous distribuez le package, vous disposez peut-ĂȘtre dĂ©jĂ du fichier setup.cfg
et vous pouvez utiliser ce fichier pour stocker la configuration Pytest. Vous verrez comment cela se fait dans l'annexe 4, «Empaquetage et distribution de projets Python», page 175.
Quel que soit le fichier dans lequel vous mettez la configuration pytest, le format sera fondamentalement le mĂȘme.
Pour pytest.ini
:
ch6 / format / pytest.ini
[pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
Pour tox.ini
:
ch6 / format / tox.ini
... tox specific stuff ... [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
Pour setup.cfg
:
ch6 / format / setup.cfg
... packaging specific stuff ... [tool:pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
La seule diffĂ©rence est que l'en-tĂȘte de section pour setup.cfg est [tool:pytest]
au lieu de [pytest]
.
Liste des options valides de fichier ini avec pytest âhelp
Vous pouvez obtenir une liste de tous les paramĂštres valides pour pytest.ini
partir de pytest --help
:
$ pytest --help ... [pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found: markers (linelist) markers for test functions empty_parameter_set_mark (string) default marker for empty parametersets norecursedirs (args) directory patterns to avoid for recursion testpaths (args) directories to search for tests when no files or directories are given in the command line. console_output_style (string) console output: classic or with additional progress information (classic|progress). usefixtures (args) list of default fixtures to be used with this project python_files (args) glob-style file patterns for Python test module discovery python_classes (args) prefixes or glob names for Python test class discovery python_functions (args) prefixes or glob names for Python test function and method discovery xfail_strict (bool) default for the strict parameter of xfail markers when not given explicitly (default: False) junit_suite_name (string) Test suite name for JUnit report junit_logging (string) Write captured log messages to JUnit report: one of no|system-out|system-err doctest_optionflags (args) option flags for doctests doctest_encoding (string) encoding used for doctest files cache_dir (string) cache directory path. filterwarnings (linelist) Each line specifies a pattern for warnings.filterwarnings. Processed after -W and --pythonwarnings. log_print (bool) default value for --no-print-logs log_level (string) default value for --log-level log_format (string) default value for --log-format log_date_format (string) default value for --log-date-format log_cli (bool) enable log display during test run (also known as "live logging"). log_cli_level (string) default value for --log-cli-level log_cli_format (string) default value for --log-cli-format log_cli_date_format (string) default value for --log-cli-date-format log_file (string) default value for --log-file log_file_level (string) default value for --log-file-level log_file_format (string) default value for --log-file-format log_file_date_format (string) default value for --log-file-date-format addopts (args) extra command line options minversion (string) minimally required pytest version xvfb_width (string) Width of the Xvfb display xvfb_height (string) Height of the Xvfb display xvfb_colordepth (string) Color depth of the Xvfb display xvfb_args (args) Additional arguments for Xvfb xvfb_xauth (bool) Generate an Xauthority token for Xvfb. Needs xauth. ...
Vous verrez tous ces paramĂštres dans ce chapitre, Ă l'exception de doctest_optionflags
, qui est abordé dans le chapitre 7, «Utilisation de pytest avec d'autres outils», page 125.
Les plugins peuvent ajouter des options de fichier ini
La liste de paramÚtres précédente n'est pas une constante. Pour les plugins (et les fichiers conftest.py), il est possible d'ajouter des options de fichier ini. Les options ajoutées seront également ajoutées à la sortie de la commande pytest --help.
Examinons maintenant quelques modifications de configuration que nous pouvons effectuer en utilisant les paramÚtres intégrés du fichier .ini disponibles dans core pytest.
Modifier les options de ligne de commande par défaut
Vous avez déjà utilisé certaines options de ligne de commande pour pytest , telles que -v/--verbose
pour la sortie verbeuse -l/--showlocals
pour afficher les variables locales avec une trace de pile pour les tests ayant Ă©chouĂ©. Vous constaterez peut-ĂȘtre que vous utilisez toujours certaines de ces optionsâor
que vous prĂ©fĂ©rez les utiliser themâfor a project
. Si vous installez addopts
dans pytest.ini
pour les paramĂštres dont vous avez besoin, vous n'avez plus Ă les saisir. Voici un ensemble que j'aime:
[pytest] addopts = -rsxX -l --tb=short --strict
Le -rsxX
permet Ă pytest de signaler les raisons de tous les tests skipped
, xfailed
ou xpassed
. Le commutateur -l
permet à pytest d'afficher une trace de pile pour les variables locales en cas de chaque échec. --tb=short
supprimera la majeure partie de la trace de la pile. Cependant, il laissera le fichier et le numéro de ligne. L' --strict
désactive l'utilisation des jetons s'ils ne sont pas enregistrés dans le fichier de configuration. Vous verrez comment procéder dans la section suivante.
Enregistrement des marqueurs pour éviter les fautes de frappe
Les marqueurs personnalisĂ©s, comme dĂ©crit dans «Ătiquetage des fonctions de test» Ă la page 31, sont parfaits pour vous permettre de marquer un sous-ensemble de tests Ă exĂ©cuter avec un marqueur spĂ©cifique. Cependant, il est trop facile de faire une erreur dans le marqueur et finalement certains tests sont marquĂ©s @pytest.mark.smoke
et certains sont marqués @pytest.mark.somke
. Par dĂ©faut, ce n'est pas une erreur. pytest pense juste que vous avez créé deux marqueurs. Cependant, cela peut ĂȘtre rĂ©solu en enregistrant des jetons dans pytest.ini, par exemple comme ceci:
[pytest] ... markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() ...
En enregistrant ces marqueurs, vous pouvez désormais les voir également avec les pytest --markers
avec leurs descriptions:
$ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --markers @pytest.mark.smoke: Run the smoke test test functions @pytest.mark.get: Run the test functions that test tasks.get() @pytest.mark.skip(reason=None): skip the ... ...
Si les marqueurs ne sont pas enregistrés, ils n'apparaßtront pas dans la liste --markers
. Lorsqu'ils sont enregistrés, ils sont affichés dans la liste et si vous utilisez --strict
, tous les jetons avec des erreurs ou non enregistrés sont affichés comme des erreurs. La seule différence entre ch6/a/tasks_proj
et ch6/b/tasks_proj
est le contenu du fichier pytest.ini. Dans ch6/a
vide. Essayons d'exécuter les tests sans enregistrer de marqueurs:
$ cd /path/to/code/ch6/a/tasks_proj/tests $ pytest --strict --tb=line ============================= test session starts ============================= collected 45 items / 2 errors =================================== ERRORS ==================================== ______________________ ERROR collecting func/test_add.py ______________________ 'smoke' not a registered marker ________________ ERROR collecting func/test_api_exceptions.py _________________ 'smoke' not a registered marker !!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 2 error in 1.10 seconds ===========================
Si vous utilisez des marqueurs dans pytest.ini
pour enregistrer vos marqueurs, vous pouvez également ajouter --strict
Ă vos addopts
pendant que vous y ĂȘtes. Tu me remercieras plus tard. Allons de l'avant et ajoutons un fichier pytest.ini au projet de tĂąches:
Si vous utilisez des marqueurs dans pytest.ini
pour enregistrer vos marqueurs, vous pouvez également ajouter --strict
Ă vos addopts
. Vous me remercierez plus tard. Continuons et ajoutons le fichier pytest.ini au projet de tĂąche:
Si vous utilisez des jetons dans pytest.ini
pour enregistrer des jetons, vous pouvez également ajouter --strict
Ă ceux existants avec addopts
. Cool?! pytest.ini
remerciements et ajoutez le fichier pytest.ini
au projet de tasks
:
ch6 / b / tasks_proj / tests / pytest.ini
[pytest] addopts = -rsxX -l --tb=short --strict markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get()
Voici la combinaison de drapeaux préférée par défaut:
-rsxX
pour -rsxX
quels tests sont ignorés, échoués ou xpassés,--tb = short
pour une trace plus courte des pannes,--strict
pour autoriser uniquement les jetons déclarés.
Et une liste de marqueurs pour le projet.
Cela devrait nous permettre d'effectuer des tests, y compris des tests de fumée:
$ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --strict -m smoke ===================== test session starts ====================== collected 57 items func/test_add.py . func/test_api_exceptions.py .. ===================== 54 tests deselected ====================== =========== 3 passed, 54 deselected in 0.06 seconds ============
Exigence minimale de Pytest
Le paramĂštre minversion
permet de spécifier la version minimale de pytest attendue pour les tests. Par exemple, j'avais l'intention d'utiliser approx()
lors du test de nombres à virgule flottante pour déterminer l'égalité «assez proche» dans les tests. Mais cette fonction n'a été introduite dans pytest qu'à partir de la version 3.0. Pour éviter toute confusion, j'ajoute ce qui suit aux projets qui utilisent approx()
:
[pytest] minversion = 3.0
Ainsi, si quelqu'un essaie d'exécuter des tests à l'aide d'une ancienne version de pytest, un message d'erreur apparaßt.
EmpĂȘchez Pytest de chercher au mauvais endroit
Saviez-vous que l'une des définitions de «recurse» est de jurer deux fois dans votre propre code? Et bien non. En fait, cela signifie tenir compte des sous-répertoires. pytest permettra la détection des tests en examinant récursivement un tas de répertoires. Mais il y a certains répertoires que vous souhaitez exclure de l'affichage de pytest.
La valeur par défaut de norecurse
est '. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
'. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
'. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
Est une bonne raison de nommer votre environnement virtuel '.venv', car tous les répertoires commençant par un point ne seront pas visibles.
Dans le cas du projet TĂąches, src
n'aura pas de mal à le spécifier, car la recherche dans les fichiers de test à l'aide de pytest sera une perte de temps.
[pytest] norecursedirs = .* venv src *.egg dist build
Lorsque vous remplacez un paramÚtre qui a déjà une valeur utile, comme ce paramÚtre, il est utile de connaßtre les valeurs par défaut et de renvoyer celles dont vous avez besoin, comme je l'ai fait dans le code précédent avec la construction *.egg dist build
.
norecursedirs
est une sorte de consĂ©quence pour les chemins de test, nous allons donc y jeter un Ćil plus tard.
tester la spécification de l'arborescence
Alors que norecursedirs
indique Ă pytest oĂč chercher, testpaths
indique Ă pytest oĂč chercher. testspaths
est une liste de répertoires relatifs au répertoire racine pour rechercher des tests. Il n'est utilisé que si le répertoire, le fichier ou le nodeid
pas spécifié comme argument.
Supposons que pour un projet Tasks
, nous plaçons pytest.ini
dans le répertoire tasks_proj
au lieu de tests:
\code\tasks_proj>tree/f . â pytest.ini â ââââsrc â ââââtasks â api.py â ... â ââââtests â conftest.py â pytest.ini â ââââfunc â test_add.py â ... â ââââunit â test_task.py â __init__.py â ...
Ensuite, il pourrait ĂȘtre judicieux de placer les tests dans des testpaths
de testpaths
:
[pytest] testpaths = tests
Maintenant, si vous exécutez pytest à partir du répertoire tasks_proj, pytest ne recherchera que dans tasks_proj/tests
. Le problÚme ici est que lors du développement et du débogage des tests, je parcours souvent le répertoire de test, donc je peux facilement tester un sous-répertoire ou un fichier sans spécifier le chemin complet. Par conséquent, cette option m'aide un peu dans les tests interactifs.
Cependant, il est idĂ©al pour les tests exĂ©cutĂ©s Ă partir d'un serveur d'intĂ©gration continue ou tox. Dans ces cas, vous savez que le rĂ©pertoire racine sera fixe et vous pouvez rĂ©pertorier les rĂ©pertoires relatifs Ă ce rĂ©pertoire racine fixe. Ce sont Ă©galement les cas oĂč vous voulez vraiment rĂ©duire le temps de test, donc se dĂ©barrasser de la recherche de tests est super.
Ă premiĂšre vue, il peut sembler idiot d'utiliser Ă la fois des chemins de test et des norecursedirs
. Cependant, comme vous l'avez déjà vu, les chemins de test aident peu dans les tests interactifs à partir de différentes parties du systÚme de fichiers. Dans ces cas, les norecursedirs
peuvent aider. De plus, si vous avez des répertoires de test qui ne contiennent pas de tests, vous pouvez utiliser norecursedirs
pour les Ă©viter. Mais en fait, quel est l'intĂ©rĂȘt de mettre des rĂ©pertoires supplĂ©mentaires dans des tests qui n'ont pas de tests?
Modification des rÚgles de détection des tests
pytest trouve les tests à exécuter en fonction de rÚgles de découverte de test spécifiques. RÚgles de détection de test standard:
⹠Commencez avec un ou plusieurs répertoires. Vous pouvez spécifier les noms de fichiers ou de répertoires sur la ligne de commande. Si vous n'avez rien spécifié, le répertoire courant est utilisé.
⹠Rechercher dans le catalogue et tous ses sous-répertoires des modules de test.
âą Un module de test est un fichier avec un nom similaire Ă test_*.py
ou *_test.py
.
âą Recherchez dans les modules de test les fonctions qui commencent par test .
⹠Recherchez les classes qui commencent par Test. Recherchez les méthodes dans les classes qui commencent par `test ,
init` .
Ce sont des rÚgles de détection standard; Cependant, vous pouvez les modifier.
python_classes
La rÚgle habituelle pour trouver des tests pour pytest et des classes est de considérer une classe comme une classe de test potentielle si elle commence par Test*
. La classe ne peut pas non plus avoir la __init__()
. Mais que se passe-t-il si nous voulons nommer nos classes de test en tant que <something>Test
ou <something>Suite
? C'est lĂ python_classes
:
[pytest] python_classes = *Test Test* *Suite
Cela nous permet de nommer les classes comme ceci:
class DeleteSuite(): def test_delete_1(): ... def test_delete_2(): ... ....
python_files
Comme pytest_classes
, python_files
modifie la rÚgle de détection de test par défaut, qui consiste à rechercher des fichiers commençant par test_*
ou ayant *_test
Ă la fin.
Supposons que vous ayez un cadre de test personnalisé dans lequel vous avez nommé tous vos fichiers de test check_<something>.py
. Semble raisonnable. Au lieu de renommer tous vos fichiers, ajoutez simplement une ligne Ă pytest.ini
comme suit:
[pytest] python_files = test_* *_test check_*
TrÚs simple. Vous pouvez maintenant transférer progressivement la convention de dénomination si vous le souhaitez, ou simplement la laisser sous check_*
.
fonctions_python
python_functions
agit comme les deux paramÚtres précédents, mais pour les fonctions de test et les noms de méthode. La valeur par défaut est test_*
. Et pour ajouter check_*
deviné - faites ceci:
[pytest] python_functions = test_* check_*
Les pytest
dénomination pytest
ne semblent pas si restrictives, n'est-ce pas? Donc, si vous n'aimez pas la convention de dénomination par défaut, changez-la simplement. Néanmoins, je vous exhorte à avoir une raison plus impérieuse pour de telles décisions. La migration de centaines de fichiers de test est certainement une bonne raison.
Interdiction XPASS
La définition de xfail_strict = true
signifie que les tests marqués avec @pytest.mark.xfail
ne sont pas reconnus comme provoquant l'erreur. Je pense que ce paramĂštre devrait toujours ĂȘtre. Pour plus d'informations sur le jeton xfail
voir «Marquage des tests en attente d'échec», page 37.
EmpĂȘcher les conflits de noms de fichiers
L'utilité d'avoir le fichier __init__.py
dans chaque sous-répertoire de test du projet m'a longtemps dérouté. Cependant, la différence entre les avoir ou ne pas les avoir est simple. Si vous avez des fichiers __init__.py
dans tous vos sous-rĂ©pertoires de test, vous pouvez avoir le mĂȘme nom de fichier de test dans plusieurs rĂ©pertoires. Et sinon, cela ne fonctionnera pas.
Voici un exemple. Les répertoires a
et b
ont tous deux le fichier test_foo.py
. Peu importe ce que ces fichiers contiennent, mais pour cet exemple, ils ressemblent Ă ceci:
ch6 / dups / a / test_foo.py
def test_a(): pass
ch6 / dups / b / test_foo.py
def test_b(): pass
Avec cette structure de répertoire:
dups âââ a â âââ test_foo.py âââ b âââ test_foo.py
Ces fichiers n'ont mĂȘme pas le mĂȘme contenu, mais les tests sont corrompus. Vous pourrez les exĂ©cuter sĂ©parĂ©ment, mais il n'y a aucun moyen d'exĂ©cuter pytest
partir du répertoire dups
:
$ cd /path/to/code/ch6/dups $ pytest a ============================= test session starts ============================= collected 1 item a\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest b ============================= test session starts ============================= collected 1 item b\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest ============================= test session starts ============================= collected 1 item / 1 errors =================================== ERRORS ==================================== _______________________ ERROR collecting b/test_foo.py ________________________ import file mismatch: imported module 'test_foo' has this __file__ attribute: /path/to/code/ch6/dups/a/test_foo.py which is not the same as the test file we want to collect: /path/to/code/ch6/dups/b/test_foo.py HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules !!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 1 error in 0.34 seconds ===========================
Rien n'est clair!
Ce message d'erreur n'indique pas ce qui s'est mal passé.
Pour corriger ce test, ajoutez simplement le fichier __init__.py
vide aux sous-répertoires. Voici un exemple du répertoire dups_fixed
avec les mĂȘmes noms de fichiers en double, mais avec des fichiers __init__.py
ajoutés:
dups_fixed/ âââ a â âââ __init__.py â âââ test_foo.py âââ b âââ __init__.py âââ test_foo.py
Essayons maintenant à nouveau à partir du niveau supérieur dans dups_fixed
:
$ cd /path/to/code/ch6/ch6/dups_fixed/ $ pytest ============================= test session starts ============================= collected 2 items a\test_foo.py . b\test_foo.py . ========================== 2 passed in 0.15 seconds ===========================
Ce sera donc mieux.
Bien sûr, vous pouvez vous convaincre que vous n'aurez jamais de noms de fichiers en double, donc cela n'a pas d'importance. Tout, comme, est normal. Mais les projets grandissent et les catalogues de tests se développent, et vous voulez vraiment attendre que cela vous arrive avant de vous en occuper? Je dis, il suffit de mettre ces fichiers là -bas. Faites-en une habitude et ne vous en souciez plus.
Exercices
Au Chapitre 5, Plugins, Ă la page 95, vous avez créé un plugin appelĂ© pytest-nice qui incluait une option de ligne de commande --nice. Ătendons cela pour inclure une option pytest.ini appelĂ©e nice.
Au chapitre 5, «Plugins» à la page 95, vous avez créé un plugin appelé pytest-nice
qui inclut l' --nice
ligne de commande --nice
. Développons ceci pour inclure l'option pytest.ini
appelée nice
.
- Ajoutez la ligne suivante Ă la fonction de crochet
pytest_addoption
pytest_nice.py
: parser.addini('nice', type='bool', help='Turn failures into opportunities.')
- Les endroits du plugin qui utilisent
getoption()
devront également appeler getini('nice')
. Apportez ces modifications. - Vérifiez cela manuellement en ajoutant
nice
au fichier pytest.ini
. - N'oubliez pas les tests de plugin. Ajoutez un test pour vérifier que le
nice
paramĂštre de pytest.ini
fonctionne correctement. - Ajoutez des tests au répertoire du plugin. Vous devez trouver des fonctionnalités supplémentaires de Pytester .
Et ensuite
Bien que pytest soit extrĂȘmement puissant en soi - en particulier avec les plugins - il s'intĂšgre Ă©galement bien avec d'autres outils de dĂ©veloppement et de test de logiciels. Dans le chapitre suivant, nous examinerons l'utilisation de pytest conjointement avec d'autres outils de test puissants.
Retour Suivant 