Configuration
第6章:配置¶
到目前为止,已经顺便讨论了影响pytest的各种非测试文件。在本章中,我们将学习影响pytest的配置文件,研究这些文件的是如何改变 pytest的行为的,并对Tasks 项目的配置文件做一些更改。
6.1 配置文件¶
在讨论如何改变pytest的默认行为之前,让我们来看看 pytest 中的所有的非测试文件,特别是谁应该关心它们。
• pytest.ini
这是pytest的主要配置文件,可以改变pytest默认行为。本章的大部分是讲述关于在pytest.ini 中可以修改的配置。
• conftest.py
conftest.py模块是一个本地插件,允许为conftest.py文件所在的目录和所有子目录提供钩子函数和夹具的地方。在第5章我们已经学过。
• __init__.py
:
当放入每个测试子目录时,这个文件允许你在多个测试目录中拥有相同的名字的测试文件。
,你会对以下内容感兴趣:
tox.ini
如果你使用tox的话。tox.ini
文件类似于 pytest.ini
,但是用于 tox。当然,你可以把 pytest 配置放在这里,避免同时使用tox.ini和pytest.ini文件,保存一个配置文件即可。
更多关于tox的内容详见【第7章:配套工具】
• setup.cfg
这是一个ini 文件样式,影响 setup.py 的行为的文件。可以在 setup.py 中添加几行代码来运行 python setup.py test 并让它运行所有的 pytest 测试。如果需要打包,可能已经有了一个setup.cfg 文件,你可以使用这个文件来存储 pytest 配置。
无论你将pytest 配置放在哪种文件中,大部分的格式都是相同的。
💡 在ch6目录中添加新目录format
,添加新的配置文件pytest.ini
:
[pytest]
addopts = -rsxX -l --tb=short --strict
xfail_strict = true
... more options ...
💡 在ch6目录中添加新目录format
,添加新的配置文件tox.ini
:
... tox specific stuff ...
[pytest]
addopts = -rsxX -l --tb=short --strict
xfail_strict = true
... more options ...
💡 在ch6目录中添加新目录format
,添加新的配置文件setup.cfg
:
... packaging specific stuff ...
[tool:pytest]
addopts = -rsxX -l --tb=short --strict
xfail_strict = true
... more options ...
setup.cfg
文件的开头是[tool:pytest]
而不是[pytest]
。
查看配置项列表¶
可以通过--help
选项,获得pytest.ini所支持的所有有效的配置项列表。
执行命令:
$ pytest --help
...省略一万字
[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file fou
nd:
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.
filterwarnings (linelist):
Each line specifies a pattern for
warnings.filterwarnings. Processed after
-W/--pythonwarnings.
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
disable_test_id_escaping_and_forfeit_all_rights_to_community_support
(bool):
disable string escape non-ascii characters,
might cause unwanted side effects(use at your
own risk)
console_output_style (string):
console output: "classic", or with additional
progress information ("progress" (percentage) |
"count").
xfail_strict (bool): default for the strict parameter of xfail
markers when not given explicitly (default:
False)
enable_assertion_pass_hook (bool):
Enables the pytest_assertion_pass hook.Make
sure to delete any previously generated pyc
cache files.
junit_suite_name (string):
Test suite name for JUnit report
junit_logging (string):
Write captured log messages to JUnit report:
one of no|log|system-out|system-err|out-err|all
junit_log_passing_tests (bool):
Capture log information for passing tests to
JUnit report:
junit_duration_report (string):
Duration time to report: one of total|call
junit_family (string):
Emit XML for schema: one of
legacy|xunit1|xunit2
doctest_optionflags (args):
option flags for doctests
doctest_encoding (string):
encoding used for doctest files
cache_dir (string): cache directory path.
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
log_auto_indent (string):
default value for --log-auto-indent
pythonpath (paths): Add paths to sys.path
faulthandler_timeout (string):
Dump the traceback of all threads if a test
takes more than TIMEOUT seconds to finish.
addopts (args): extra command line options
minversion (string): minimally required pytest version
required_plugins (args):
plugins that must be present for pytest to run
environment variables:
PYTEST_ADDOPTS extra command line options
PYTEST_PLUGINS comma-separated plugins to load during start
up
PYTEST_DISABLE_PLUGIN_AUTOLOAD set to disable plugin auto-loading
PYTEST_DEBUG set to enable debug tracing of pytest's inte
rnals
to see available markers type: pytest --markers
to see available fixtures type: pytest --fixtures
(shown according to specified file_or_dir or current dir if not specifi
ed; fixtures with leading '_' are only shown with the '-v' option
[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:
这句之后的,展示的是pytest.ini文件中所有的可用的选项。
在本章中将讲述除doctest_optionflags外的配置项,doctest_optionflags将在第七章中介绍。
6.2 定制配置项¶
前面的设置列表不是固定不变的。插件和conftest.py都可以定制ini文件选项。通过定制添加的选项也会被添加到pytest --help的输出信息中。
addopts¶
添加命令选项
到目前为止,我们已经使用过了很多的pytest命令行选项,比如-v详细输出。我们会经常在一个项目中频繁使用这些选项。如果不想每次执行命令的时候都重复输入该选项,那么就可以在pytest.ini配置文件中,使用addopts
添加自己想要的选项,这样就不必在每次执行的时候动手输入它们了。
💡 在ch6目录中添加新目录tests
,然后将ch1
中的两个测试测试文件test_one.py
和test_two.py
复制粘贴进来。
常规执行命令:
$ cd ch6/tests
$ pytest
======================== test session starts =========================
platform win32 -- Python 3.8.8, pytest-7.1.1, pluggy-1.0.0
rootdir: D:\Coding\Gitees\studypytest\ch6\tests, configfile: pytest.ini
plugins: nice-0.1.0
collected 2 items
test_one.py . [ 50%]
test_two.py F [100%]
============================== 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 more diff
test_two.py:2: AssertionError
====================== short test summary info =======================
FAILED test_two.py::test_failing - assert (1, 2, 3) == (3, 2, 1)
==================== 1 failed, 1 passed in 0.10s =====================
如果不需要展示报错信息,并展示详细结果,常规做法是,执行时添加--tb=no
和-s
选项。
$ cd ch6/tests
$ pytest --tb=no -v
--tb=no
表示屏蔽报错信息;-v
表示展示详细结果信息。
输出结果:
======================== test session starts =========================
platform win32 -- Python 3.8.8, pytest-7.1.1, pluggy-1.0.0 -- d:\system
\envs\mytasks\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\Coding\Gitees\studypytest\ch6\tests, configfile: pytest.ini
plugins: nice-0.1.0
collected 2 items
test_one.py::test_passing PASSED [ 50%]
test_two.py::test_failing FAILED [100%]
====================== short test summary info =======================
FAILED test_two.py::test_failing - assert (1, 2, 3) == (3, 2, 1)
==================== 1 failed, 1 passed in 0.03s =====================
也可以通过添加pytest.ini
配置文件配置执行命令时需要添加的选项,做到一劳永逸。
💡 在ch6/tests
目录中添加新的配置文件pytest.ini
:
[pytest]
addopts = --tb=no -v
再次执行时,就不需要添加额外的参数了:
$ cd ch6/tests
$ pytest
======================== test session starts =========================
platform win32 -- Python 3.8.8, pytest-7.1.1, pluggy-1.0.0 -- d:\system
\envs\mytasks\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\Coding\Gitees\studypytest\ch6\tests, configfile: pytest.ini
plugins: nice-0.1.0
collected 2 items
test_one.py::test_passing PASSED [ 50%]
test_two.py::test_failing FAILED [100%]
====================== short test summary info =======================
FAILED test_two.py::test_failing - assert (1, 2, 3) == (3, 2, 1)
==================== 1 failed, 1 passed in 0.03s =====================
markers¶
注册标记
自定义标记可以用来筛选运行测试子集。但是,很容易拼错一个标记,比如小手一抖,容易搞出一些函数用@pytest.mark.smoke
标记,而又有一些用@pytest.mark.somke
标记的测试函数。问题是,pytest它也不会报错,而是认为这是想要创建两个不同的标记。如果由此产生莫名其妙的测试结果,通常会让人非常困惑,又很难排查问题。有个好办法避免这个隐患,就是通过在 pytest.ini 中注册标记来解决。
💡 在ch6/tests
目录中添加新的目录markers_demo
,在其中添加新的配置文件:
[pytest]
markers =
smoke: Run the smoke test functions for tasks project
目前
注册好标记之后,现在也可以通过pytest --markers
看到它们的描述信息。
macOS系统执行命令:
$ cd register
$ pytest --markers | grep smoke
$ pytest --markers | findstr smoke
@pytest.mark.smoke: Run the smoke test functions for tasks project
pytest --markers
结果列表中。
如果执行用例时使用了--strict-markers
选项,任何带有拼写错误的标记,或未注册的标记都会被视为错误。
温馨提示:英文版原书中此处使用的是
--strict
选项是即将废弃的,官方推荐使用--strict-markers
代替它。
💡 在ch6/tests/markers_demo
目录中添加新的测试模块test_markers.py
:
import pytest
@pytest.mark.clifford
def test_markers():
assert True
$ cd ch6/tests/markers_demo
$ pytest --strict-markers --tb=short
======================== test session starts =========================
platform win32 -- Python 3.8.8, pytest-7.1.1, pluggy-1.0.0
rootdir: D:\Coding\Gitees\studypytest\ch6\tests\markers_demo, configfil
e: pytest.ini
plugins: nice-0.1.0
collected 0 items / 1 error
=============================== ERRORS ===============================
__________________ ERROR collecting test_markers.py __________________
'clifford' not found in `markers` configuration option
====================== short test summary info =======================
ERROR test_markers.py
!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!
========================== 1 error in 0.13s ==========================
要想解决修复这个错误,其实很简单,只需要在配置文件中,将clifford标记进行注册即可。
💡 修改ch6/tests/markers_demo
目录中的配置文件pytest.ini
,注册clifford
标记:
[pytest]
markers =
smoke: Run the smoke test functions for tasks project
clifford: Run the test functions with clifford marker
执行命令:
$ cd ch6/tests/markers_demo
$ pytest --strict-markers
======================== test session starts =========================
platform win32 -- Python 3.8.8, pytest-7.1.1, pluggy-1.0.0
rootdir: D:\Coding\Gitees\studypytest\ch6\tests\markers_demo, configfil
e: pytest.ini
plugins: nice-0.1.0
collected 1 item
test_markers.py . [100%]
========================= 1 passed in 0.02s ==========================
minversion¶
最低版本要求
minversion 允许你指定测试所需的最低的pytest 版本。
例如,approx()
在测试浮点数是否足够接近时非常好用,但是这个特性直到 pytest3.0版本才被引入。
💡 在ch6/tests/minversion_demo
目录中,添加新的测试模块test_float.py
:
from pytest import approx
def test_float_approx():
assert 1.1 + 2.2 == approx(3.3)
为了避免误用,可以使用了approx()
的项目中添加了配置信息。
[pytest]
minversion = 3.0
ERROR: D:\Coding\Gitees\studypytest\ch6\tests\miniversion_demo\pytest.i
ni: 'minversion' requires pytest-9.0, actual pytest-2.1.1'
norecursedirs¶
测试目录黑名单
屏蔽某个目录,防止 pytest 找错地方。
recurse是指递归地遍历目录,那么norecurse就是不要递归遍历目录的意思。对于 pytest 来说,测试发现过程默认会递归地遍历许多目录来收集符合规则的测试函数。但是项目中总会有一些目录明显不需要pytest去递归查找用例的,比如说源码目录src,根本没有任何测试代码。
norecurse 的默认设置是'.* build dist CVS _darcs {arch} and *.egg
。虚拟环境通常命名为.venv
,所有以点号开头的目录都不会被遍历。然而,我有一个习惯,把它命名为venv,所以我可以把它添加到 norecursedirs 中。
对于Tasks 项目,你也可以在其中添加 src目录,因为让 pytest 在非测试代码中查找测试文件只会浪费时间。
[pytest]
norecursedirs = .* venv src *.egg dist build
当重写一个已经存在合法值的设置项时,最好知道默认值是什么,然后把你关心的设置放回去,就像我在前面的代码中用*.egg dist build
的那样。
💡 在ch6/tests/
目录中,添加新的目录norecursedirs_demo
,然后创建新目录one,添加一个新的测试模块test_one.py:
# ch6/tests/norecursedirs_demo/one/test_one.py
def test_passing():
assert (1, 2, 3) == (1, 2, 3)
ch6/tests/norecursedirs_demo
目录中,添加新的目录two
,添加一个新的测试模块test_two.py:
# ch6/tests/norecursedirs_demo/one/test_two.py
def test_failing():
assert (1, 2, 3) == (3, 2, 1)
执行收集用例的命令:
$ cd ch6/tests/norecursedirs_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/norecursedirs_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 2 items
<Module one/test_one.py>
<Function test_passing>
<Module two/test_two.py>
<Function test_failing>
======================== 2 tests collected in 0.01s ========================
💡 在ch6/tests/norecursedirs_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
norecursedirs = one
$ cd ch6/tests/norecursedirs_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/norecursedirs_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
<Module two/test_two.py>
<Function test_failing>
======================== 1 test collected in 0.01s =========================
testpaths¶
Specifying Test Directory Locations
测试目录白名单
相对于 norecursedirs
告诉 pytest 不要查看哪里,testpaths
则告诉 pytest 要查看哪里。
testspaths
是一个相对于根目录的目录列表,用于查找测试用例。只有当运行pytest时未指定任何的目录、文件或nodeid 参数时才会生效。
💡 在ch6/tests/
目录中,添加新的目录testpaths_demo
,然后创建新目录one,添加一个新的测试模块test_one.py:
# ch6/tests/testpaths_demo/one/test_one.py
def test_passing():
assert (1, 2, 3) == (1, 2, 3)
ch6/tests/norecursedirs_demo
目录中,添加新的目录two
,添加一个新的测试模块test_two.py:
# ch6/tests/testpaths_demo/one/test_two.py
def test_failing():
assert (1, 2, 3) == (3, 2, 1)
执行收集用例的命令:
$ cd ch6/tests/testpaths_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/testpaths_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 2 items
<Module one/test_one.py>
<Function test_passing>
<Module two/test_two.py>
<Function test_failing>
======================== 2 tests collected in 0.02s ========================
💡 在ch6/tests/testpaths_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
testpaths = one
$ cd ch6/tests/testpaths_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/testpaths_demo, configfile: pytest.ini, testpaths: one
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
<Module one/test_one.py>
<Function test_passing>
======================== 1 test collected in 0.01s =========================
不过,这种方式的问题在于,如果经常在测试开发和调试期间在测试目录之间跳来跳去,可以很容易地测试某个子目录或文件,而不需要输出整个路径。因此,这个设置对交互式测试来说可能派不上用场。
然而,它对于从持续集成服务器,或者 tox 运行的测试非常有用。在这些情况下,根目录是固定的,你可以列出相对于根目录的相对目录。如果你确实需要压缩你的测试时间,所以减少一点测试发现的耗时是非常棒的。
乍一看,在配置文件中同时使用测试路径 testpaths
和 norecursedirs
似乎很愚蠢。然而,正如你所看到的,testpaths 对于来自文件系统不同部分的交互式测试帮助不大。在这种情况下,norecursedirs 可以提供帮助。另外,如果在tests目录中存在不包含测试文件的目录,你可以使用 norecursedirs 来避免测试这些奇葩的目录。话说回头,在tests目录中添加一些不含测试用例的目录有什么意义呢?
6.3 定制发现规则¶
Changing Test Discovery Rules
pytest 根据特定的测试发现规则查找需要运行的测试用例。
标准的测试发现规则是:
- 从一个或多个目录开始。
你可以在命令行中指定文件名或目录名。如果不指定任何内容,则使用当前工作目录。
- 在目录和所有子目录中递归查找测试模块
测试模块是以test_
开头或 _test
结尾的py文件。
- 在测试模块中寻找以
test_
开头的测试函数。 - 在测试模块中寻找以
Test
开头但不包含__init__
方法的测试类,在类中寻找以test_开头的测试方法。
这些是标准的发现规则,当然,我们可以更改这些规则。
python_files¶
默认的测试模块的发现规则,即查找以test_*
开头或以 *_test
结尾的py文件。python_files
配置项则可以修改这个规则。
假设有一个自定义测试框架,在该框架中所有测试文件都是像check_xxx.py
这样命名的。只需要在pytest.ini 中添加这样一行,就能避免重命名所有的测试文件。
💡 在ch6/tests
目录中,添加新的目录pyton_files_demo
,添加一个新的测试模块check_demo.py
:
# ch6/tests/python_files_demo/check_demo.py
def test_files_rule():
assert True
$ cd ch6/tests/python_files_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/python_files_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 0 items
======================= no tests collected in 0.01s ========================
💡 在ch6/tests/python_files_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
python_files = check_*
$ cd ch6/tests/python_files_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/python_files_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
<Module check_demo.py>
<Function test_files_rule>
======================== 1 test collected in 0.01s =========================
check_
开头的测试模块check_demo.py
。
python_classes¶
对于pytest 和 class类而言,通常的测试发现规则是,如果一个类以 Test
开头,那么它就是一个潜在的测试类,并且这个类不能包含__init__()
函数。
但是,如果偏偏就不按套路出牌,想把测试类进行非主流命名,比如以xxxTest
结尾,或以 xxxSuite
结果,该怎么办呢?这就是 python_classes
配置项的用武之地。
💡 在ch6/tests
目录中,添加新的目录pyton_classes_demo
,添加一个新的测试模块test_classes_demo.py
:
# ch6/tests/pyton_classes_demo/test_classes_demo.py
class DemoSuite:
def test_classes_rule(self):
self.flag = True
assert self.flag is True
$ cd ch6/tests/python_classes_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/pyton_classes_demo
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 0 items
======================= no tests collected in 0.01s ========================
💡 在ch6/tests/python_classes_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
python_classes = *Suite
$ cd ch6/tests/python_classes_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/pyton_classes_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
<Module test_classes_demo.py>
<Class DemoSuite>
<Function test_classes_rule>
======================== 1 test collected in 0.01s =========================
*Suite
结尾的测试类DemoSuite
中的测试方法。
python_functions¶
python_functions
配置项作用于测试函数和测试方法名称收集规则。默认的测试函数和测试方法的收集规则是test _*
。
💡 在ch6/tests
目录中,添加新的目录pyton_functions_demo
,添加一个新的测试模块test_functions_demo.py
:
# ch6/tests/pyton_functions_demo/test_functions_demo.py
def check_function():
assert True
class TestFunctionsRule:
def check_method(self):
assert True
$ cd ch6/tests/python_functions_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/pytest_functions_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 0 items
======================= no tests collected in 0.01s ========================
如果需要支持 check_*
的函数命名则需按照下面这样添加配置。
💡 在ch6/tests/python_functions_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
python_functions = check_*
$ cd ch6/tests/python_functions_demo
$ pytest --co
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/pytest_functions_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 2 items
<Module test_functions_demo.py>
<Function check_function>
<Class TestFunctionsRule>
<Function check_method>
======================== 2 tests collected in 0.01s ========================
check_
开头的测试函数check_function
和测试方法check_method
。
现在看来,pytest 命名规范看起来没有那么死板。如果你不喜欢默认的命名规范,那就改变命名规范。比如,迁移数或者重命名成百上千的现成测试文件绝对是一个很轻松的活儿。
6.4 禁用 XPASS¶
xfail_strict
设置 xfail_strict=true
会导致,当标记为 @pytest.mark.xfail的测试函数执行失败后不会被报告为错误。通常建议始终添加这个配置项。
💡 在ch6/tests
目录中,添加新的目录xfail_strict_demo
,添加一个新的测试模块test_demo.py
:
# ch6/tests/xfail_strict_demo/test_demo.py
import pytest
# 预期失败,实际执行成功 XPASS
@pytest.mark.xfail(reason="预期失败,实际成功")
def test_exp_fail_but_passed():
assert True
$ cd ch6/tests/xfail_strict_demo
$ pytest -v
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1 -- /Users/xiaofo/Envs/pytest/bin/python3
cachedir: .pytest_cache
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/xfail_strict_demo
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
test_demo.py::test_exp_fail_but_passed XPASS (预期失败,实际成功) [100%]
============================ 1 xpassed in 0.01s ============================
💡 在ch6/tests/python_functions_demo
目录中,添加新的配置文件pytest.ini
:
[pytest]
xfail_strict=True
$ cd ch6/tests/xfail_strict_demo
$ pytest -v
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1 -- /Users/xiaofo/Envs/pytest/bin/python3
cachedir: .pytest_cache
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/xfail_strict_demo, configfile: pytest.ini
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
test_demo.py::test_exp_fail_but_passed FAILED [100%]
================================= FAILURES =================================
_________________________ test_exp_fail_but_passed _________________________
[XPASS(strict)] 预期失败,实际成功
========================= short test summary info ==========================
FAILED test_demo.py::test_exp_fail_but_passed
============================ 1 failed in 0.01s =============================
6.5 避免文件名冲突¶
Avoiding Filename Collisions
在一个项目的每个测试子目录中都有一个__init__.py文件,可能会让人困惑。然而,有这些文件和有这些文件的区别是什么?如果在所有的测试子目录中有__init__.py
文件,则可以在多个目录中使用同名的测试模块。如果没有__init__.py
文件则不行。就是这么简单,这就是__init__.py
文件的影响。
💡 在ch6/tests
目录中,添加新的目录dups_demo/dups_before/a
,在其中添加新的测试模块test_the_same.py
:
def test_a():
pass
💡 在
ch6/tests/dups_demo/dups_before目录中,添加一个新的目录
b,在其中添加新的测试模块
test_the_same.py`:
def test_b():
pass
当前dups_before
目录结构如下:
dups_before
├── a
│ └── test_same_name.py
└── b
└── test_same_name.py
a
和目录 b
都有一个test_same_name.py
测试模块。这个模块中包含什么测试函数其实不重要。
单独指定运行目录a
或目录b
是可以的。
执行命令:
$ cd ch6/dups_demo/dups_before
$ pytest a
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_before
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
a/test_same_name.py . [100%]
============================ 1 passed in 0.01s =============================
执行命令:
$ cd ch6/dups_demo/dups_before
$ pytest b
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_before
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item
b/test_same_name.py . [100%]
============================ 1 passed in 0.01s =============================
但是从dups_before
目录直接运行 pytest 则不可以。
执行命令:
$ cd ch6/dups_demo/dups_before
$ pytest
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_before
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 1 item / 1 error
================================== ERRORS ==================================
___________________ ERROR collecting b/test_same_name.py ___________________
import file mismatch:
imported module 'test_same_name' has this __file__ attribute:
/Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_before/a/test_same_name.py
which is not the same as the test file we want to collect:
/Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_before/b/test_same_name.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules
========================= short test summary info ==========================
ERROR b/test_same_name.py
!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!
============================= 1 error in 0.04s =============================
要解决这个测试模块同名问题,只需要在子目录中添加添加空的__init__.py
文件。
💡 在ch6/tests/dups_demo
目录中,将dups_before目录整个复制一份并粘贴为dups_after
,然后分别在它的字目录a
和子目录b
中添加空的__init__.py
文件,也就是说把目录a
和目录b
变成包package。
当前dups_after
目录的结构:
dups_after
├── a
│ ├── __init__.py
│ └── test_foo.py
└── b
├── __init__.py
└── test_foo.py
进入dups_after
执行命令:
$ cd ch6/dups_demo/dups_after
$ pytest
=========================== test session starts ============================
platform darwin -- Python 3.9.7, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/xiaofo/coding/Personal/studypytest/ch6/tests/dups_demo/dups_after
plugins: allure-pytest-2.9.45, nice-0.1.0
collected 2 items
a/test_same_name.py . [ 50%]
b/test_same_name.py . [100%]
============================ 2 passed in 0.01s =============================
也许在你的项目中永远不会存在同名的测试模块文件,但是没关系。随着项目的迭代,测试目录在增多,真的想等到这个模块同名问题出现再修复它吗?建议添加__init__.py文件,作为一种习惯,这样就一劳永逸了。
pytest 不仅本身非常强大,尤其是插件,而且它也能很好地与其他软件测试工具集成在一起。
在下一章中,你将看到 pytest 是如何与其他强大的测试工具的结合使用的。