返回 下一个 
在本章中,我们将研究影响pytest的配置文件,讨论pytest如何基于它们更改其行为,以及对Tasks项目的配置任务进行一些更改。

本书中的示例是使用Python 3.6和pytest 3.2编写的。 pytest 3.2支持Python 2.6、2.7和Python 3.3+。
本书网页上的链接 pragprog.com上提供了Tasks项目以及本书中显示的所有测试的源代码。 您无需下载源代码即可了解测试代码。 示例中以方便的形式提供了测试代码。 但是,为了跟上项目的任务,或者改编测试示例来测试自己的项目(不费力气!),您必须转到本书的网页并下载工作。 在该书的网页上,有一个勘误信息链接和一个论坛 。
在剧透下方是该系列文章的列表。
构型
到目前为止,在本书中,我已经讨论了各种主要影响pytest的非测试文件,除了conftest.py例外,我在第95页的第5章插件中对此进行了详细介绍。在本章中,我们将介绍配置文件,影响pytest的内容,讨论pytest如何基于它们更改其行为,并对Tasks项目配置文件进行一些更改。
了解pytest配置文件
在我告诉您如何更改pytest中的默认行为之前,让我们先看一下pytest中的所有非测试文件,尤其是谁应该照顾它们。
您应该了解以下内容:
- pytest.ini :这是主要的Pytest配置文件,允许您更改默认行为。 由于您可以进行很多配置更改,因此本章的大部分内容专门介绍您可以在
pytest.ini
进行的设置。 - conftest.py :这是一个本地插件,允许将钩子函数和装置连接到
conftest.py
文件及其所有子目录所在的目录。 conftest.py
文件在第95页第5章“插件”中进行了描述。 __init__.py
:放置在每个测试子目录中时,此文件使您可以在多个测试目录中使用相同的测试文件名。 我们将在第120页上的文章“避免文件名冲突”中查看一个示例,该示例在测试目录中没有__init__.py
文件的情况下会出现问题。
如果您使用毒素,您将对以下内容感兴趣:
- tox.ini :此文件类似于
pytest.ini
,但用于tox
。 但是,您可以将pytest
配置pytest
而不是同时拥有tox.ini
文件和pytest.ini
文件,从而为您节省一个配置文件。 第125页第7章“与其他工具一起使用pytest”中讨论了Tox。
如果您要分发Python软件包(例如Tasks),则需要关注此文件:
- setup.cfg :这也是一个会影响
setup.py
行为的INI文件。 您可以在setup.py
添加几行以运行python setup.py test
并运行所有pytest测试。 如果您正在分发软件包,则可能已经有了setup.cfg
文件,可以使用此文件存储Pytest配置。 您将在第175页的附录4“打包和分发Python项目”中看到如何完成此操作。
无论您将pytest配置放入哪个文件,格式都基本相同。
对于pytest.ini
:
ch6 /格式/ pytest.ini
[pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
对于tox.ini
:
ch6 /格式/ tox.ini
... tox specific stuff ... [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
对于setup.cfg
:
ch6 /格式/ setup.cfg
... packaging specific stuff ... [tool:pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ...
唯一的区别是setup.cfg的节头是[tool:pytest]
而不是[pytest]
。
使用pytest –help列出有效的ini文件选项
您可以从pytest --help
获取pytest.ini
的所有有效参数的列表:
$ 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. ...
除了doctest_optionflags
(在第125页的第7章“将pytest与其他工具一起使用”中讨论)外,您将在本章中看到所有这些设置。
插件可以添加ini文件选项
先前的设置列表不是常数。 对于插件(和conftest.py文件),可以添加ini文件选项。 添加的选项也将添加到pytest --help命令的输出中。
现在,让我们看一下使用核心pytest中可用的.ini文件的内置设置可以进行的一些配置更改。
更改默认命令行选项
您已经为pytest使用了一些命令行选项,例如-v/--verbose
用于详细输出-l/--showlocals
来查看带有堆栈跟踪的局部变量以进行失败的测试。 您可能会发现, them—for a project
,您始终会使用其中一些options—or
更喜欢使用them—for a project
。 如果在pytest.ini
安装了pytest.ini
作为所需的参数,则无需再输入它们。 这是我喜欢的一套:
[pytest] addopts = -rsxX -l --tb=short --strict
-rsxX
允许pytest报告所有skipped
, xfailed
或xpassed
测试的原因。 -l
开关允许pytest在每次失败的情况下显示局部变量的堆栈跟踪。 --tb=short
将删除大部分堆栈跟踪。 但是,它将保留文件和行号。 如果未在配置文件中注册令牌,则--strict
禁止使用令牌。 您将在下一节中看到如何执行此操作。
标记注册,避免标记输入错误
自定义标记(如第31页的“标记测试功能”中所述)非常适合让您标记要使用特定标记运行的测试子集。 但是,在标记中犯错误太容易了,最终有些测试被标记为@pytest.mark.smoke
,有些被标记为@pytest.mark.somke
。 默认情况下,这不是错误。 pytest只是认为您创建了两个标记。 但是,可以通过在pytest.ini中注册令牌来解决此问题,例如:
[pytest] ... markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() ...
通过注册这些标记,您现在还可以使用pytest --markers
以及它们的描述来查看它们:
$ 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 ... ...
如果未注册标记,则它们不会出现在--markers
列表中。 注册它们时,将列出它们,如果使用--strict
,则任何带有错误或未注册的令牌都将显示为错误。 ch6/a/tasks_proj
和ch6/b/tasks_proj
之间的唯一区别是pytest.ini文件的内容。 在ch6/a
空。 让我们尝试在不注册任何标记的情况下运行测试:
$ 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 ===========================
如果您在pytest.ini
使用标记来注册标记,则pytest.ini
在使用时在pytest.ini
中添加--strict
。 待会儿我会谢谢你的。 让我们继续并将pytest.ini文件添加到任务项目:
如果您在pytest.ini
使用标记来注册标记,则还可以在addopts
添加--strict
。 您稍后会感谢我。 让我们继续并将pytest.ini文件添加到任务项目中:
如果您在pytest.ini
使用令牌注册令牌,则还可以使用addopts
将--strict
添加到现有addopts
。 酷吗? pytest.ini
感谢,并将pytest.ini
文件添加到tasks
项目:
ch6 / b / task_proj /测试/ 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()
这是默认情况下首选的标志组合:
-rsxX
跳过,xfailed或xpass的测试,--tb = short
用于更短地跟踪故障,--strict
只允许声明的令牌。
以及该项目的标记列表。
这应该使我们能够进行测试,包括烟雾测试:
$ 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 ============
最低Pytest要求
使用minversion
参数可以指定测试所需的最低pytest版本。 例如,我打算在测试浮点数时使用approx()
来确定测试中的“相当接近”的相等性。 但是直到版本3.0才在pytest中引入此功能。 为避免混淆,我向使用approx()
项目添加了以下内容:
[pytest] minversion = 3.0
因此,如果有人尝试使用较旧版本的pytest运行测试,则会出现错误消息。
停止pytest在错误的地方搜索
您是否知道“递归”的定义之一是在自己的代码中宣誓两次? 好吧,不。 实际上,这意味着要考虑子目录。 pytest将通过递归检查一堆目录来启用测试检测。 但是有些目录要从pytest中排除。
norecurse
的默认值为'. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
'. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
'. * Build dist CVS _darcs {arch} and *.egg. Having '.*'
是命名虚拟环境“ .venv”的一个很好的理由,因为所有以点开头的目录都将不可见。
对于Tasks项目, src
可以轻松指定,因为使用pytest搜索测试文件将浪费时间。
[pytest] norecursedirs = .* venv src *.egg dist build
当覆盖已经具有有用值的参数(例如该参数)时,了解默认值并返回所需的值非常有用,就像我在上一个代码中使用*.egg dist build
所做的那样。
norecursedirs
是测试路径的一种结果,因此让我们稍后看一下。
测试目录树规范
虽然norecursedirs
告诉pytest看哪里,而testpaths
告诉pytest看哪里。 testspaths
是相对于用于查找测试的根目录的目录列表。 仅当未将目录,文件或nodeid
指定为参数时才使用它。
假设对于Tasks
项目,我们将pytest.ini
在tasks_proj
目录中而不是测试中:
\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 │ ...
然后将测试放在测试testpaths
可能是有意义的:
[pytest] testpaths = tests
现在,如果您从tasks_proj
目录运行pytest,pytest将仅在tasks_proj/tests
搜索。 这里的问题是,在测试的开发和调试过程中,我经常遍历测试目录,因此无需指定完整路径即可轻松测试子目录或文件。 因此,此选项对交互式测试有一定帮助。
但是,这对于从连续集成服务器或Tox运行的测试非常有用。 在这些情况下,您知道根目录将是固定的,并且可以列出相对于该固定根目录的目录。 在您确实希望减少测试时间的情况下,也是如此,因此摆脱对测试的搜索是一件很不错的事情。
乍一看,同时使用测试路径和norecursedirs
似乎很愚蠢。 但是,正如您已经看到的,测试路径在文件系统不同部分的交互式测试中几乎没有帮助。 在这些情况下,无norecursedirs
可能会有所帮助。 另外,如果您的测试目录不包含测试,则可以使用norecursedirs
来避免使用它们。 但是实际上,将多余的目录放在没有测试的测试中有什么意义呢?
更改测试检测规则
pytest根据特定的测试发现规则查找要运行的测试。 标准测试检测规则:
•从一个或多个目录开始。 您可以在命令行上指定文件或目录的名称。 如果您未指定任何内容,则使用当前目录。
•在目录及其所有子目录中搜索测试模块。
•测试模块是一个名称类似于test_*.py
*_test.py
或*_test.py
。
•在测试模块中查找以test开头的功能。
•查找以Test开头的类。 在以`test开始 ,
init` ,
类中寻找 ,
。
这些是标准的检测规则; 但是,您可以更改它们。
python_classes
查找pytest和类测试的通常规则是,如果该类以Test*
开头,则将其视为潜在的测试类。 该类也不能具有__init__()
方法。 但是,如果我们要将测试类命名为<something>Test
或<something>Suite
怎么办? 这是python_classes
出现python_classes
:
[pytest] python_classes = *Test Test* *Suite
这使我们可以像这样命名类:
class DeleteSuite(): def test_delete_1(): ... def test_delete_2(): ... ....
python_files
像pytest_classes
一样, python_files
修改默认的测试检测规则,该规则包括查找以test_*
开头或以*_test
test_*
结尾的文件。
假设您有一个自定义的测试框架,在其中将所有测试文件命名为check_<something>.py
。 似乎合理。 无需重命名所有文件,只需在pytest.ini
添加一行,如下所示:
[pytest] python_files = test_* *_test check_*
很简单 现在,您可以根据需要逐步转移命名约定,或者将其保留为check_*
。
python_functions
python_functions
行为类似于之前的两个设置,但用于测试函数和方法名称。 默认值为test_*
。 并添加check_*
猜对了),请执行以下操作:
[pytest] python_functions = test_* check_*
pytest
命名pytest
似乎没有那么严格,对吗? 因此,如果您不喜欢默认的命名约定,只需对其进行更改。 不过,我敦促您为此类决定提供更有说服力的理由。 迁移数百个测试文件绝对是一个很好的理由。
XPASS禁令
设置xfail_strict = true
表示标记为@pytest.mark.xfail
的测试未被识别为导致该错误。 我认为此设置应始终如此。 有关xfail
令牌的更多信息xfail
请参阅第37页“标记等待失败的测试”。
防止文件名冲突
在项目的每个测试子目录中都有__init__.py
文件的作用使我困惑了很长时间。 但是,拥有或不拥有它们的区别很简单。 如果所有测试子目录中都有__init__.py
文件,则可以在多个目录中使用相同的测试文件名。 如果没有,那么这将行不通。
这是一个例子。 目录a
和b
都有文件test_foo.py
。 这些文件包含什么都没有关系,但是在本示例中,它们看起来像这样:
ch6 / dups / a / test_foo.py
def test_a(): pass
ch6 / dups / b / test_foo.py
def test_b(): pass
使用此目录结构:
dups ├── a │ └── test_foo.py └── b └── test_foo.py
这些文件甚至没有相同的内容,但是测试已损坏。 您将能够分别运行它们,但是无法从dups
目录运行pytest
:
$ 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 ===========================
不清楚!
此错误消息并不表示出了什么问题。
要修复此测试,只需将空的__init__.py
文件添加到子目录中。 这是dups_fixed
目录的示例,该目录具有相同的重复文件名,但添加了__init__.py
文件:
dups_fixed/ ├── a │ ├── __init__.py │ └── test_foo.py └── b ├── __init__.py └── test_foo.py
现在,让我们从dups_fixed
的顶层重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 ===========================
这样会更好。
当然,您可以说服自己永远不会有重复的文件名,所以没关系。 一切都正常。 但是项目在增长,测试目录也在增长,您肯定要等它发生之后再处理它吗? 我说,就是把这些文件放在那里。 养成习惯,不必担心。
练习题
在第95页的第5章,插件中,您创建了一个名为pytest-nice的插件,其中包括--nice命令行选项。 让我们对其进行扩展,使其包含一个名为nice的pytest.ini选项。
在第95页第5章“插件”中,您创建了一个名为pytest-nice
的插件,其中包括--nice
命令行--nice
。 让我们将其扩展为包含名为nice
的pytest.ini
选项。
pytest_addoption
pytest_nice.py
添加到pytest_addoption
pytest_nice.py
挂钩函数中: parser.addini('nice', type='bool', help='Turn failures into opportunities.')
- 插件中使用
getoption()
也必须调用getini('nice')
。 进行这些更改。 - 通过向
pytest.ini
文件添加nice
来手动验证。 - 不要忘记插件测试。 添加一个测试以验证
pytest.ini
中的nice
参数pytest.ini
正常运行。 - 将测试添加到插件目录。 您需要找到一些其他的Pytester功能 。
接下来是什么
尽管pytest本身非常强大(尤其是插件),但它也可以与其他软件开发和软件测试工具很好地集成。 在下一章中,我们将结合其他强大的测试工具来研究pytest的用法。
返回 下一个 