使用和调用¶
通过调用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 显示多个修订版的测试结果的网页。
向在线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()
不建议使用同一进程(例如为了重新运行测试)。