- Pytest:帮助您编写更好的程序
- 完整的 Pytest 文档
- 安装和入门
- 使用和调用
- 在现有测试套件中使用 pytest
- 测试中断言的编写和报告
- Pytest 夹具:显式、模块化、可扩展
- 用属性标记测试函数
- MonkeyPatching / Mocking 模块和环境
- 临时目录和文件
- 捕获 stdout/stderr 输出
- 捕获警告
- 模块和测试文件的 Doctest 集成
- 跳过和 xfail:处理无法成功的测试
- 参数化夹具和测试功能
- 缓存:使用交叉测试运行状态
- UnitTest.TestCase 支持
- 运行为鼻子编写的测试
- 经典的 Xunit 风格设置
- 安装和使用插件
- 编写插件
- 登录
- 良好的集成实践
- 古怪的测试
- Pytest 导入机制和 sys.path/PYTHONPATH
- 设置 bash 完成
- API 引用
- _pytest.hookspec
- _pytest.python_api
- _pytest.outcomes
- _pytest.config
- _pytest.mark
- _pytest.recwarn
- _pytest.assertion
- _pytest.freeze_support
- _pytest.fixtures
- _pytest.cacheprovider
- _pytest.capture
- _pytest.doctest
- _pytest.junitxml
- _pytest.logging
- _pytest.monkeypatch
- _pytest.pytester
- _pytest.tmpdir
- _pytest.python
- _pytest.nodes
- _pytest.reports
- _pytest._code.code
- _pytest.config.argparsing
- _pytest.main
- pluggy.callers
- _pytest.config.exceptions
- py.test 2.0.0:断言++、UnitTest++、Reporting++、Config++、Docs++
- 示例和自定义技巧
- 配置
- 贡献开始
- 向后兼容策略
- Python 2.7 和 3.4 支持
- 企业版 pytest
- 项目实例
- 历史笔记
- 弃用和移除
- 发展指南
- 演讲和辅导
使用和调用
通过调用pytest python -m pytest
您可以从命令行通过python解释器调用测试:
python -m pytest [...]
这几乎等同于调用命令行脚本 pytest [...]
直接,除了通过 python
还将当前目录添加到 sys.path
.
可能的出口代码
运行 pytest
可能导致六种不同的退出代码:
- 退出代码0
所有测试都已收集并成功通过
- 退出代码1
测试已收集并运行,但有些测试失败
- 退出代码2
测试执行被用户中断
- 退出代码3
执行测试时发生内部错误
- 退出代码4
pytest命令行使用错误
- 退出代码5
未收集任何测试
他们的代表是 pytest.ExitCode
枚举。作为公共API的一部分的出口代码可以通过以下方式直接导入和访问:
from pytest import ExitCode
注解
如果您想在某些情况下自定义退出代码,特别是在没有收集测试的情况下,请考虑使用 pytest-custom_exit_code 插件。
获取有关版本、选项名称、环境变量的帮助
pytest --version # shows where pytest was imported from pytest --fixtures # show available builtin function arguments pytest -h | --help # show help on command line and config file options
完整的命令行标志可以在 reference .
第一次(或n次)故障后停止
在第一(n)次失败后停止测试过程:
pytest -x # stop after first failure pytest --maxfail=2 # stop after two failures
指定测试/选择测试
pytest支持几种方法来运行和从命令行中选择测试。
在模块中运行测试
pytest test_mod.py
在目录中运行测试
pytest testing/
按关键字表达式运行测试
pytest -k "MyClass and not method"
这将运行包含与给定名称匹配的名称的测试 字符串表达式 (不区分大小写),它可以包括使用文件名、类名和函数名作为变量的Python运算符。上面的例子将运行 TestMyClass.test_something
但不是 TestMyClass.test_method_simple
.
按节点ID运行测试
每个收集的测试都被分配一个唯一的 nodeid
它由模块文件名和诸如类名、函数名和参数化参数等说明符组成,用 ::
字符。
在模块内运行特定测试:
pytest test_mod.py::test_func
在命令行中指定测试方法的另一个示例:
pytest test_mod.py::TestClass::test_method
Run tests by marker expressions
pytest -m slow
将运行所有用 @pytest.mark.slow
装饰符。
有关详细信息,请参阅 marks .
从包运行测试
pytest --pyargs pkg.testing
这将导入 pkg.testing
并使用其文件系统位置来查找和运行测试。
修改python回溯打印
修改回溯打印的示例:
pytest --showlocals # show local variables in tracebacks pytest -l # show local variables (shortcut) pytest --tb=auto # (default) 'long' tracebacks for the first and last # entry, but 'short' style for the other entries pytest --tb=long # exhaustive, informative traceback formatting pytest --tb=short # shorter traceback format pytest --tb=line # only one line per failure pytest --tb=native # Python standard library formatting pytest --tb=no # no traceback at all
这个 --full-trace
导致在出错时打印很长的跟踪(比 --tb=long
)它还确保在 KeyboardInterrupt (CTRL+C)。如果测试花费的时间太长,并且您使用ctrl+c中断它们以找出测试的位置,那么这非常有用。 悬挂 . 默认情况下,不会显示任何输出(因为键盘中断被pytest捕获)。通过使用此选项,可以确保显示跟踪。
详细总结报告
这个 -r
标记可用于在测试会话结束时显示 简短测试摘要信息 ,使大型测试套件中的所有故障、跳过、xfails等的清晰图像变得容易。
它默认为 fE
列出失败和错误。
例子:
# content of test_example.py import pytest @pytest.fixture def error_fixture(): assert 0 def test_ok(): print("ok") def test_fail(): assert 0 def test_error(error_fixture): pass def test_skip(): pytest.skip("skipping this test") def test_xfail(): pytest.xfail("xfailing this test") @pytest.mark.xfail(reason="always xfail") def test_xpass(): pass
$ pytest -ra =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ========================= short test summary info ========================== SKIPPED [1] test_example.py:22: skipping this test XFAIL test_example.py::test_xfail reason: xfailing this test XPASS test_example.py::test_xpass always xfail ERROR test_example.py::test_error - assert 0 FAILED test_example.py::test_fail - assert 0 == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
这个 -r
选项接受其后面的字符数,使用 a
上面的意思是 除通行证外的所有通行证 。
以下是可用字符的完整列表:
f
-失败
E
-误差
s
跳过
x
-失败
X
-XPASS
p
通过
P
-通过输出
用于(取消)选择组的特殊字符:
a
- all exceptpP
A
-所有
N
-无,这不能用来显示任何内容(因为fE
是默认设置)
可以使用多个字符,例如,要只查看失败和跳过的测试,可以执行:
$ pytest -rfs =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ========================= short test summary info ========================== FAILED test_example.py::test_fail - assert 0 SKIPPED [1] test_example.py:22: skipping this test == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
使用 p
列出通过的测试,同时 P
添加一个额外的 通过 部分,其中包含那些通过但已捕获输出的测试:
$ pytest -rpP =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ================================== PASSES ================================== _________________________________ test_ok __________________________________ --------------------------- Captured stdout call --------------------------- ok ========================= short test summary info ========================== PASSED test_example.py::test_ok == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s ===
下降到 PDB (python debugger)失败时
python附带一个内置的python调试器,名为 PDB. pytest
允许一个人掉进 PDB 通过命令行选项提示:
pytest --pdb
这将在每次失败(或键盘中断)时调用Python调试器。通常,您可能只想在第一次失败测试中这样做,以了解特定的失败情况:
pytest -x --pdb # drop to PDB on first failure, then end test session pytest --pdb --maxfail=3 # drop to PDB for first three failures
请注意,在任何失败时,异常信息都存储在 sys.last_value
, sys.last_type
和 sys.last_traceback
. 在交互使用中,这允许使用任何调试工具进入后期调试。还可以手动访问异常信息,例如:
>>> import sys >>> sys.last_traceback.tb_lineno 42 >>> sys.last_value AssertionError('assert result == "ok"',)
下降到 PDB (python调试器)在测试开始时
pytest
允许一个人掉进 PDB 通过命令行选项在每次测试开始时立即提示:
pytest --trace
这将在每次测试开始时调用Python调试器。
设置断点
要在代码中设置断点,请使用本机python import pdb;pdb.set_trace()
调用代码,pytest会自动禁用该测试的输出捕获:
其他测试中的输出捕获不受影响。
任何先前的测试输出已经被捕获并将被处理。
结束调试器会话(通过
continue
命令)。
使用内置断点函数
python 3.7引入了一个内置的 breakpoint()
功能。pytest支持使用 breakpoint()
有以下行为:
什么时候?
breakpoint()
被称为PYTHONBREAKPOINT
设置为默认值时,Pytest将使用自定义内部PDB跟踪UI而不是系统默认值Pdb
.测试完成后,系统将默认返回系统。
Pdb
跟踪用户界面。用
--pdb
传递到pytest时,自定义内部PDB跟踪UI与breakpoint()
以及失败的测试/未处理的异常。
--pdbcls
可用于指定自定义调试器类。
分析测试执行持续时间
在 6.0 版更改.
要获取长度超过1.0秒的最慢10个测试持续时间的列表:
pytest --durations=10 --durations-min=1.0
默认情况下,pytest不会显示太小的测试持续时间(<0.005s),除非 -vv
在命令行上传递。
故障处理程序
5.0 新版功能.
这个 faulthandler 标准模块可用于在segfault或超时后转储python跟踪。
模块将自动启用pytest运行,除非 -p no:faulthandler
在命令行上给出。
同时 faulthandler_timeout=X
如果测试时间超过 X
完成时间(在Windows上不可用)。
注解
此功能已从外部集成 pytest-faulthandler 插件,有两个小的区别:
要禁用它,请使用
-p no:faulthandler
而不是--no-faulthandler
:前者可以与任何插件一起使用,因此它保存了一个选项。这个
--faulthandler-timeout
命令行选项已成为faulthandler_timeout
配置选项。它仍然可以从命令行配置,使用-o faulthandler_timeout=X
.
Warning about unraisable exceptions and unhandled thread exceptions
6.2 新版功能.
注解
这些功能仅适用于Python>=3.8。
未处理的异常是在无法传播到调用方的情况下引发的异常。最常见的情况是在 __del__
实施。
未处理的线程异常是在 Thread
但未处理,导致线程不干净地终止。
这两种类型的异常通常都被认为是bug,但可能不会被注意到,因为它们不会导致程序本身崩溃。Pytest检测到这些情况,并发出在测试运行摘要中可见的警告。
插件将自动启用pytest运行,除非 -p no:unraisableexception
(适用于不能提出要求的例外情况)及 -p no:threadexception
(对于线程异常)选项在命令行上给出。
警告可以有选择地使用 pytest.mark.filterwarnings 马克。警告类别为 pytest.PytestUnraisableExceptionWarning
和 pytest.PytestUnhandledThreadExceptionWarning
.
创建JUnitXML格式文件
创建可被读取的结果文件 Jenkins 或其他持续集成服务器,使用此调用:
pytest --junitxml=path
在以下位置创建XML文件 path
.
要设置根测试套件XML项的名称,可以配置 junit_suite_name
配置文件中的选项:
[pytest] junit_suite_name = my_suite
4.0 新版功能.
JUnit XML规范似乎表明 "time"
属性应该报告总的测试执行时间,包括安装和拆卸 (1 , 2 )它是默认的pytest行为。要只报告调用持续时间,请配置 junit_duration_report
这样的选择:
[pytest] junit_duration_report = call
record_property
如果要记录测试的其他信息,可以使用 record_property
固定装置:
def test_function(record_property): record_property("example_key", 1) assert True
这将添加一个额外属性 example_key="1"
到生成的 testcase
标签:
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009"> <properties> <property name="example_key" value="1" /> </properties> </testcase>
或者,您可以将此功能与自定义标记集成:
# content of conftest.py def pytest_collection_modifyitems(session, config, items): for item in items: for marker in item.iter_markers(name="test_id"): test_id = marker.args[0] item.user_properties.append(("test_id", test_id))
在你的测试中:
# content of test_function.py import pytest @pytest.mark.test_id(1501) def test_function(): assert True
将导致:
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009"> <properties> <property name="test_id" value="1501" /> </properties> </testcase>
警告
请注意,使用此功能将中断对最新JUnitXML架构的架构验证。当与某些CI服务器一起使用时,这可能是一个问题。
record_xml_attribute
要向testcase元素添加额外的xml属性,可以使用 record_xml_attribute
固定装置。这也可用于覆盖现有值:
def test_function(record_xml_attribute): record_xml_attribute("assertions", "REQ-1234") record_xml_attribute("classname", "custom_classname") print("hello world") assert True
不像 record_property
,这不会添加新的子元素。相反,这将添加一个属性 assertions="REQ-1234"
在生成的 testcase
标记并重写默认值 classname
具有 "classname=custom_classname"
:
<testcase classname="custom_classname" file="test_function.py" line="0" name="test_function" time="0.003" assertions="REQ-1234"> <system-out> hello world </system-out> </testcase>
警告
record_xml_attribute
是一个实验性的特性,它的接口在未来的版本中可能会被更强大和通用的东西所取代。然而,功能本身将保持不变。
使用此 record_xml_property
在使用CI工具分析XML报告时可以提供帮助。但是,一些解析器对允许的元素和属性非常严格。许多工具使用XSD模式(如下面的示例)来验证传入的XML。确保您使用的是解析器允许的属性名。
下面是Jenkins用于验证XML报告的方案:
<xs:element name="testcase"> <xs:complexType> <xs:sequence> <xs:element ref="skipped" minOccurs="0" maxOccurs="1"/> <xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/> <xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/> <xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/> <xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> <xs:attribute name="name" type="xs:string" use="required"/> <xs:attribute name="assertions" type="xs:string" use="optional"/> <xs:attribute name="time" type="xs:string" use="optional"/> <xs:attribute name="classname" type="xs:string" use="optional"/> <xs:attribute name="status" type="xs:string" use="optional"/> </xs:complexType> </xs:element>
警告
请注意,使用此功能将中断对最新JUnitXML架构的架构验证。当与某些CI服务器一起使用时,这可能是一个问题。
record_testsuite_property
4.5 新版功能.
如果要在测试套件级别添加属性节点,该节点可能包含与所有测试相关的属性,则可以使用 record_testsuite_property
会话范围的夹具:
这个 record_testsuite_property
会话范围的fixture可用于添加与所有测试相关的属性。
import pytest @pytest.fixture(scope="session", autouse=True) def log_global_env_facts(record_testsuite_property): record_testsuite_property("ARCH", "PPC") record_testsuite_property("STORAGE_TYPE", "CEPH") class TestMe: def test_foo(self): assert True
夹具是一个可调用的 name
和 value
A的 <property>
在生成的XML的测试套件级别添加的标记:
<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006"> <properties> <property name="ARCH" value="PPC"/> <property name="STORAGE_TYPE" value="CEPH"/> </properties> <testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/> </testsuite>
name
必须是字符串, value
将转换为字符串并正确进行XML转义。
生成的XML与最新的 xunit
标准,与 record_property 和 record_xml_attribute .
创建结果日志格式文件
要创建纯文本机器可读的结果文件,可以发出:
pytest --resultlog=path
看看里面的内容 path
位置。此类文件由 PyPy-test 显示多个修订版的测试结果的网页。
警告
此选项很少使用,并计划在pytest 6.0中删除。
如果使用此选项,请考虑使用新的 pytest-reportlog 而是插件。
见 the deprecation docs 更多信息。
向在线Pastebin服务发送测试报告
为每个测试失败创建URL :
pytest --pastebin=failed
这将向远程粘贴服务提交测试运行信息,并为每个失败提供一个URL。您可以像往常一样选择测试或添加例如 -x
如果你只想发送一个特定的失败。
为整个测试会话日志创建URL :
pytest --pastebin=all
当前仅实现粘贴到http://bpaste.net服务。
在 5.2 版更改.
如果由于任何原因创建URL失败,将生成一个警告,而不是使整个测试套件失败。
早期加载插件
您可以在命令行中通过 -p
选项:
pytest -p mypluginmodule
选项接收 name
参数,可以是:
例如,完整的模块点状名称
myproject.plugins
. 此虚线名称必须是可导入的。插件的入口点名称。这是传递给
setuptools
when the plugin is registered. For example to early-load the pytest-cov 您可以使用的插件:pytest -p pytest_cov
禁用插件
要在调用时禁用加载特定插件,请使用 -p
选项和前缀 no:
.
示例:禁用加载插件 doctest
,负责从文本文件执行doctest测试,调用pytest如下:
pytest -p no:doctest
从python代码调用pytest
你可以调用 pytest
直接从Python代码:
pytest.main()
这就像从命令行调用 pytest 。它不会上升 SystemExit
但返回exitcode。您可以传入选项和参数:
pytest.main(["-x", "mytestdir"])
您可以指定其他插件到 pytest.main
:
# content of myinvoke.py import pytest class MyPlugin: def pytest_sessionfinish(self): print("*** test run reporting finishing") pytest.main(["-qq"], plugins=[MyPlugin()])
运行它会显示 MyPlugin
添加并调用其挂钩:
$ python myinvoke.py .FEsxX. [100%]*** test run reporting finishing ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ========================= short test summary info ========================== FAILED test_example.py::test_fail - assert 0 ERROR test_example.py::test_error - assert 0
注解
调用 pytest.main()
将导致导入您的测试和它们导入的任何模块。由于python的导入系统具有缓存机制,因此随后调用 pytest.main()
来自同一进程的不会反映调用之间对这些文件的更改。因此,多次调用 pytest.main()
不建议使用同一进程(例如为了重新运行测试)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论