1. 通过python -m pytest
调用pytest
你可以通过python的解释器来执行测试:
python -m pytest
这和直接执行pytest [...]
命令的效果几乎是一样的。
2. pytest
执行结束时返回的状态码
运行pytest可能导致六种不同的退出代码:
- ExitCode0:所有收集到的测试用例通过
- ExitCode1:测试已收集并运行,但有些测试失败
- ExitCode2:测试执行被用户中断
- ExitCode3:执行测试时发生内部错误
- ExitCode4:pytest命令使用错误
- ExitCode5:未收集测试用例
ExitCode可以通过以下方式直接导入和访问:
from pytest import ExitCode
3. 获取版本信息
pytest --version # 查看版本号和pytest的引入路径
4. 获取内置函数参数
pytest --fixtures
6. 获取帮助信息
pytest -h | --help # 查看帮助信息
6. 在第一次(或n次)故障后停止
pytest可以设置在第一(n)次失败后停止测试过程:
pytest -x #第一次测试失败后停止测试
pytest --maxfail=2 #第二次测试失败后停止测试
7. 指定/选择测试用例
在模块中运行测试
pytest test_mod.py
在目录中运行测试
pytest testing/
按关键字表达式运行测试
pytest -k "MyClass and not method"
# 示例
class TestMyClass:
def test_something(self):
assert 0
def test_method_simple(self):
assert True
> pytest -k "MyClass and not method" TestMyClass.py
================================================= test session starts =================================================
platform win32 -- Python 3.9.9, pytest-7.0.1, pluggy-1.0.0
rootdir: D:\Code\Pytest
plugins: allure-pytest-2.9.45, html-3.1.1, metadata-1.11.0
collected 2 items / 1 deselected / 1 selected
TestMyClass.py F [100%]
====================================================== FAILURES =======================================================
_____________________________________________ TestMyClass.test_something ______________________________________________
self = <TestMyClass.TestMyClass object at 0x000002803C5C9FD0>
def test_something(self):
> assert 0
E assert 0
TestMyClass.py:3: AssertionError
=============================================== short test summary info ===============================================
FAILED TestMyClass.py::TestMyClass::test_something - assert 0
=========================================== 1 failed, 1 deselected in 0.09s ===========================================
这将运行包含与给定名称匹配的名称的测试字符串表达式(不区分大小写),它可以包括使用文件名、类名和函数名作为变量的Python运算符。上面的例子将运行TestClass.test_something
而不是TestClass.test_method_simple
8. 按节点ID运行测试
pytest
为每一个收集到的测试用例指定一个唯一的nodeid
,它由模块名加说明符构成,中间用::
间隔。其中,说明符可以是类名、函数名以及由parametrize
标记赋予的参数
# test_nodeid.py
import pytest
def test_one():
print("test_one")
assert 1
class TestNodeId:
def test_two(self):
print("TestNodeId::test_two")
assert 1
@pytest.mark.parametrize('x,y',[(1,1),(3,4)])
def test_three(self,x,y):
print(f"TestNodeId::test_three::{x} == {y}")
assert x == y
上述实例中,我们创建了三个测试用例,分别对应不同的说明符:
-
指定函数名执行
>pytest -q -s test_nodeid.py::test_one 2022-03-12 18:48:31 test_one . 1 passed in 0.01s
-
指定类名+函数名执行
> pytest -q -s test_nodeid.py::TestNodeId::test_two 2022-03-12 18:50:27 TestNodeId::test_two . 1 passed in 0.00s
-
指定由
parametrize
标记赋予的参数执行> pytest -q -s test_nodeid.py::TestNodeId::test_three[1-1] 2022-03-12 18:54:50 TestNodeId::test_three::1 == 1 . 1 passed in 0.00s
这里对参数x
、y
赋值的形式是[1-1]
,中间以-
间隔;
单个或多个参数的赋值形式类比,并且只能为[1-1]
或者[3-4]
,其他的会报错。
注意:
这里也可以使用
-k
选项达到同样的效果:
- 首先,可以使用
--collect-only
选项查看用例名:> pytest -q -s --collect-only test_nodeid.py 2022-03-12 19:02:12 test_nodeid.py::test_one test_nodeid.py::TestNodeId::test_two test_nodeid.py::TestNodeId::test_three[1-1] test_nodeid.py::TestNodeId::test_three[3-4]
- 然后,使用
-k
执行符合规则的用例,例如:执行test_nodeid.py::test_one
:> pytest -q -s -k 'test_one and not TestNodeId' ./test_nodeid.py 2022-03-12 19:04:29 test_one . 1 passed, 3 deselected in 0.00s
结果和执行
pytest -q -s test_nodeid.py::test_one
效果一样。
8.1 执行指定标记的用例
pytest -m slow
这种方式会运行所有通过装饰器@pytest.mark.slow
进行装饰的测试用例。
8.2 执行指定包中的测试用例
pytest --pyargs pkg.testing
这种方式会导入pkg.testing
,并且基于该包所在位置来查找并运行测试用例
9. 修改Python回溯打印
pytest -l, --showlocals # 打印本地变量
pytest --tb=auto # 默认模式
pytest --tb=long # 尽可能详细输出
pytest --tb=short # 更简短的输出
pytest --tb=line # 每个失败信息总结在一行中
pytest --tb=native # Python 的标准输出
pytest --tb=no # 不打印失败信息
--full-trace
是一种比--tb=long
更详细的输出模式。甚至能观察到用户打断执行(Ctrl + C)时的回溯信息,而上述六种模式默认是不输出此类信息的。
10. 详细总结报告
使用-r
选项可以在执行结束后,打印一个简短的总结报告。在执行测试用例很多时,可以让你对结果有个清晰的了解(包含所有故障、跳过、xfails等)
# test_report.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.xfail("xfailing this test")
@pytest.mark.xfail(reason="always xfail")
def test_xpass():
passs
> pytest -q -rA .\test_report.py
.FExx [100%]
======================================================= ERRORS ========================================================
____________________________________________ ERROR at setup of test_error _____________________________________________
@pytest.fixture
def error_fixture():
> assert 0
E assert 0
test_report.py:4: AssertionError
====================================================== FAILURES =======================================================
______________________________________________________ test_fail ______________________________________________________
def test_fail():
> assert 0
E assert 0
test_report.py:10: AssertionError
======================================================= PASSES ========================================================
_______________________________________________________ test_ok _______________________________________________________
------------------------------------------------ Captured stdout call -------------------------------------------------
ok
=============================================== short test summary info ===============================================
PASSED test_report.py::test_ok
XFAIL test_report.py::test_skip
reason: xfailing this test
XFAIL test_report.py::test_xpass
always xfail
ERROR test_report.py::test_error - assert 0
FAILED test_report.py::test_fail - assert 0
1 failed, 1 passed, 2 xfailed, 1 error in 0.11s
-r
选项后面要紧接这一个参数,用于过滤显示测试用例的结果。
以下是所有有效的字符参数:
- f:失败的
- E:出错的
- s:跳过执行的
- x:跳过执行,并标记为xfailed的
- X:跳过执行,并标记为xpassed的
- p:测试通过的
- P:测试通过,并且有输出信息的;即用例中有
print
等 - a:除了测试通过的,其他所有的;即除了
p
和P
的 - A:所有的
上述字符参数可以叠加使用,例如:我们期望过滤出失败的和未执行的:
pytest -rfs
11. 失败时加载PDB(Python Debugger)环境
PDB
是Python
内建的诊断器,pytest
允许通过以下命令在执行失败时进入这个诊断器模式:
pytest --pdb
pytest
会在测试用例失败(或者Ctrl + C)时,调用这个诊断器:
# \Code\Pytest\test_pdb.py
def test_fail():
x = 1
assert x == 0
> pytest -q --pdb .\test_pdb.py
F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def test_fail():
x = 1
> assert x == 0
E assert 1 == 0
test_pdb.py:3: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB post_mortem (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> d:\code\pytest\test_pdb.py(3)test_fail()
-> assert x == 0
(Pdb) x
1
(Pdb) import sys
(Pdb) sys.last_value
AssertionError('assert 1 == 0')
(Pdb) sys.last_type
<class 'AssertionError'>
(Pdb) sys.last_traceback
<traceback object at 0x000001CDBB27E900>
(Pdb)
你还可以访问测试用例的本地变量x
;
失败信息存储在sys.value,sys.last_type,sys.last_traceback
变量中,你可以在交互环境中访问它们;
使用exit
命令,退出PDB
环境。
12. 开始执行时就加载PDB
环境
通过以下命令,pytest
允许你在每个测试用例开始执行时,就加载PDB
环境:
pytest --trace
13. 设置断点
在测试用例代码中添加import pdb;pdb.set_trace()
,当其被调用时,pytest
会停止这条用例的输出:
- 其他用例不受影响
- 通过
continue
命令,退出PDB
环境,并继续执行用例;
14. 使用内置的中断函数
Python有一个内置breakpoint()
函数。pytest可以在以下场景中支持使用:
- 当
breakpoint
被调用,并且PYTHONBREAKPOINT
为None
时,pytest
会使用内部自定义的PDB
代替系统的; - 测试执行结束时,自动切回系统自带的
PDB
- 当加上
--pdb
选项时,breakpoint
和测试发生错误时,都会调用内部自定义的PDB
--pdbcls
选项允许指定一个用户自定义的PDB
类
15. 分析测试执行时间
获取执行最慢的10个测试用例:
pytest --durations=10
默认情况下,pytest
不会显示执行时间<0.01s的测试用例,可以使用-vv
选项查看它们
16. 错误句柄
在测试执行中发生段错误或者超时的情况下,faulthandler
标准模块可以转储python
的回溯信息;它在pytest
的执行中默认使能,使用-p no:faulthandler
选项可以关闭它;同样,faulthandler_timeout=x
配置项,可用于当测试用例的完成时间超过x
秒时,转储所有线程的python
回溯信息:
# pytest.ini
[pytest]
faulthandler_timeout=5
配置测试执行的超时时间是5秒;
import time
def test_faulthandler():
time.sleep(7)
assert 1
测试用例中添加等待7秒的操作;
-
默认使能
faulthandler
的情况:> pytest -q .\test_fault_handler.py Timeout (0:00:05)! Thread 0x000024c8 (most recent call first): File "D:\Code\Pytest\test_fault_handler.py", line 3 in test_faulthandler File "D:\Software\Python\Python39\Lib\site-packages\_pytest\python.py", line 192 in pytest_pyfunc_call File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__ File "D:\Software\Python\Python39\Lib\site-packages\_pytest\python.py", line 1718 in runtest File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 168 in pytest_runtest_call File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__ File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 261 in <lambda> File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 340 in from_call File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 260 in call_runtest_hook File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 221 in call_and_report File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 132 in runtestprotocol File "D:\Software\Python\Python39\Lib\site-packages\_pytest\runner.py", line 113 in pytest_runtest_protocol File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__ File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 347 in pytest_runtestloop File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__ File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 322 in _main File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 268 in wrap_session File "D:\Software\Python\Python39\Lib\site-packages\_pytest\main.py", line 315 in pytest_cmdline_main File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_callers.py", line 39 in _multicall File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_manager.py", line 80 in _hookexec File "D:\Software\Python\Python39\Lib\site-packages\pluggy\_hooks.py", line 265 in __call__ File "D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py", line 165 in main File "D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py", line 188 in console_main File "D:\Software\Python\Python39\Scripts\pytest.exe\__main__.py", line 7 in <module> File "D:\Software\Python\Python39\lib\runpy.py", line 87 in _run_code File "D:\Software\Python\Python39\lib\runpy.py", line 197 in _run_module_as_main . [100%] 1 passed in 7.02s
在执行刚超过5秒的时候打印出回溯信息。但不会中断测试的执行;
-
去使能
faulthandler
的情况:> pytest -q -p no:faulthandler .\test_fault_handler.py . [100%] ====================== warnings summary ======================= ..\..\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py:1249 D:\Software\Python\Python39\Lib\site-packages\_pytest\config\__init__.py:1249: PytestConfigWarning: Unknown config option: faulthandler_timeout self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 1 passed, 1 warning in 7.02s
超时并不会触发回溯信息的打印。
注意:
这个功能是从pytest-faulthandler插件合并而来的,但是有两点不同:
- 去使能时,使用
-p no:faulthandler
代替原来的--no-faulthandler
- 使用
faulthandler_timeout
配置项代替--faulthandler-timeout
命令行选项来配置超时时间。当然,你也可以使用-o faulthandler_timeout=x
在命令行配置;
17. 创建JunitXML
格式的测试报告
使用如下命令,可以在指定的path
中创建一个能被Jenkins
或者其他CI
工具读取的XML
格式的测试报告:
pytest --junitxml=path
你可以在项目的pytest.ini
文件中,通过设置junit_suite_name
的值,自定义XML
文件中testsuite
根节点的name
信息:
[pytest]
junit_suite_name = pytest_chinese_doc
执行一个测试用例test_nodeid.py::test_one
看看效果:
> pytest -q --junitxml=.\report\test_one.xml test_nodeid.py::test_one
生成的XML
测试报告:
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
<testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.007" timestamp="2022-03-14T10:16:40.235646" hostname="LAPTOP-LEQLNICU">
<testcase classname="test_nodeid" name="test_one" time="0.000" />
</testsuite>
</testsuites>
我们可以看到,<testsuite>
节点的name
属性的值,变为我们所期望的pytest_chinese_doc
,而不是默认的pytest
;Junit XML规定time
属性应该表明测试用例执行的全部耗时,包含setup
和teardown
中的操作,这也是pytest的默认行为;如果你只想记录测试用例执行的时间,只需要作如下配置:
# pytest.ini
junit_duration_report = call
18. 在报告中为测试用例附加额外的子节点信息
我们有两种方式实现这个功能:
-
使用
record_property fixture
:为
test_record_property
用例添加一个额外的testid
:# test_xml_report.py def test_record_property(record_property): record_property("test_id",10010) assert 1
在报告中表现为
<property name="test_id" value="10010" />
<?xml version="1.0" encoding="utf-8"?> <testsuites> <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.018" timestamp="2022-03-14T11:20:12.295733" hostname="LAPTOP-LEQLNICU"> <testcase classname="test_xml_report" name="test_record_property" time="0.001"> <properties> <property name="test_id" value="10010" /> </properties> </testcase> </testsuite> </testsuites>
-
解析一个自定义的标记
@pytest.mark.test_id()
:首先,修改
pytest_collection_modifyitems
钩子方法,添加对test_id
标记的支持:# conftest.py def pytest_collection_modifyitems(session,config,items): for item in items: for maker in item.iter_makers(name="test_id"): test_id = maker.args[0] item.user_properties.append("test_id",test_id)
然后,修改测试用例:
import pytest @pytest.mark.test_id(10010) def test_record_property1(): assert 1
执行测试用例
> pytest -q --junitxml=.\report\test_record_property1.xml test_xml_report.py . [100%] ==================================================================== warnings summary ===================================================================== test_xml_report.py:8 D:\Code\Pytest\test_xml_report.py:8: PytestUnknownMarkWarning: Unknown pytest.mark.test_id - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html @pytest.mark.test_id(10010) -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property1.xml ------------------------------------------- 1 passed, 1 warning in 0.01s
在报告中的也表现为
<property name="test_id" value="10010" />
:<?xml version="1.0" encoding="utf-8"?> <testsuites> <testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.016" timestamp="2022-03-15T09:29:04.823852" hostname="LAPTOP-LEQLNICU"> <testcase classname="test_xml_report" name="test_record_property1" time="0.000"> <properties> <property name="test_id" value="10010" /> </properties> </testcase> </testsuite> </testsuites>
注意:这里我们会收到一个警告:
PytestUnknownMarkWarning: Unknown pytest.mark.test_id - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
@pytest.mark.test_id(10010)– Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
这是因为我们没有在
pytest
中注册test_id
标记,但不影响正常的执行;如果你想去除这个警告,只需要在
pytest.ini
的配置文件中注册这个标记:[pytest] markers= test_id:为测试用例添加ID
注意:
变动后的报告可能不符合最新的
JUnitXML
的模式检查规则,导致在某些CI工具上可能会发生未知的错误。
19. 在报告中为测试用例附加额外的属性信息
可以通过record_xml_attribute fixture
为测试用例附加额外的属性,而不像record_property
为其添加子节点;
为测试用例添加一个test_id
属性,并修改原先的classname
属性:
# test_xml_report.py
def test_record_property2(record_property):
record_xml_attibute('test_id',10010)
record_xml_attribute('classname','custom_classname')
assert 1
执行测试用例
> pytest -q --junitxml=.\report\test_record_property2.xml test_xml_report.py
. [100%]
==================================================================== warnings summary =====================================================================
test_xml_report.py::test_record_property2
test_xml_report.py:13: PytestExperimentalApiWarning: record_xml_attribute is an experimental feature
def test_record_property2(record_xml_attribute):
test_xml_report.py::test_record_property2
test_xml_report.py:13: PytestWarning: record_xml_attribute is incompatible with junit_family 'xunit2' (use 'legacy' or 'xunit1')
def test_record_property2(record_xml_attribute):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property2.xml -------------------------------------------
1 passed, 2 warnings in 0.02s
在报告中的表现为<testcase classname="custom_classname" name="test_record_property2" time="0.005" />
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
<testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.020" timestamp="2022-03-15T11:10:35.459391" hostname="LAPTOP-LEQLNICU">
<testcase classname="custom_classname" name="test_record_property2" time="0.005" />
</testsuite>
</testsuites>
注意:
record_xml_attribute
目前是一个实验性的功能,未来可能被更强大的APU所替代,但功能本身会被保留。- 变动后的报告可能不符合最新的
JUnitXML
的模式检查规则,导致在某些CI工具上可能会发生未知的错误。
20. 在报告中为测试集附加额外的子节点信息
可以通过自定义一个session
作用域级别的fixture
,为测试集添加子节点信息,并且会作用于所有的测试用例;
这个自定义的fixture
需要调用另外一个record_testsuite_property fixture
:
record_testsuite_property
接收两个参数name
和value
以构成<property>
标签,其中,name
必须以字符串,value
会转换为字符串并进行XML转义:
# test_xml_report.py
@pytest.fixture(scope="session")
def log_global_env_facts(record_testsuite_property):
record_testsuite_property("EXECUTOR","luizyao")
record_testsuite_property("LOCATION","NJ")
def test_record_property3(log_global_env_facts):
asserrt 1
执行测试用例:
> pytest -q --junitxml=.\report\test_record_property3.xml test_xml_report.py
. [100%]
------------------------------------------- generated xml file: D:\Code\Pytest\report\test_record_property3.xml -------------------------------------------
1 passed in 0.02s
生成的测试报告表现为:在testsuite
节点中,多了一个properties
子节点,包含所有新增的属性节点,而且,它和所有的testcase
节点是平级的:
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
<testsuite name="pytest_chinese_doc" errors="0" failures="0" skipped="0" tests="1" time="0.022" timestamp="2022-03-15T11:22:56.744263" hostname="LAPTOP-LEQLNICU">
<properties>
<property name="EXECUTOR" value="luizyao" />
<property name="LOCATION" value="NJ" />
</properties>
<testcase classname="test_xml_report" name="test_record_property3" time="0.007" />
</testsuite>
</testsuites>
注意:
这样生成的XML文件是符合最新的
xunit
标准的,这点和record_property
、record_xml_attribute
正好相反;
21. 创建纯文本格式的测试报告
不推荐使用,计划在
pytest6.0
中删除这个功能
使用如下命令,可以在指定的path
中创建一个纯文本的测试报告:
pytest --resultlog=path
22. 为测试报告提供URL
链接–pastebin
服务
目前,只实现了在http://bpaste.net上的展示功能;
-
为每一个失败的测试用例创建一个URL
pytest --pastebin=path
也可以通过添加
-x
选项,只为第一个失败的测试用例创建一个URL; -
为所有的测试用例创建一个URL
pytest --pastebin=all
23. 尽早的加载插件
你可以在命令行中使用-p
选项,来尽早的加载某一个插件:
pytest -p mypluginmodule
-p
选项接收一个name
参数,这个参数可以为:
-
一个完整的本地插件引入,例如:
myproject.plugins
,其必须是可以import
的。 -
一个公共插件的名称,这是其注册时在
setuptools
中赋予的名字,例如:尽早的加载pytest-cov
插件:pytest -p pytest _cov
24. 去使能插件
你可以在命令行中使用-p
结合no
:,来去使能一个插件的加载,例如:
pytest -p no:doctest
25. 在Python
代码中调用pytest
可以直接在代码中调用pytest
:
pytest.main()
这和你在命令行中执行pytest .
几乎是一样的,但其也有以下特点:
-
不会触发
SystemExit
,而是返回exitcode
:import time def test_one(): time.sleep(10) if __name__ == "__main__": import pytest ret = pytest.main(['-q',__file__]) print("pytest.main() 返回pytest.ExitCode.INTERRUPTED:",ret==pytest.ExitCode.INTERRUPTED)
用例中有等待10s的操作,在这期间,打断执行(Ctrl+C),
pytest.main()
返回的是INTERRUPTED
状态码:> python .\invoke_via_main.py !!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!! d:\Code\Pytest\invoke_via_main.py:4: KeyboardInterrupt (to show a full traceback on KeyboardInterrupt use --full-trace) no tests ran in 6.51s pytest.main() 返回pytest.ExitCode.INTERRUPTED: True
-
传递选项和参数:
pytest.main(["-x","mytestdir"])
-
指定一个插件:
import pytest class MyPlugin: def pytest_sessionfinish(self): print("*** test run reporting finishing") pytest.main(["-qq"],plugins=[MyPlugin()])
注意:
调用
pytest.main()
会引入你的测试文件以及其所引用的所有模块。用于Python引入机制的缓存特性,当这些文件发生变化时,后续再调用pytest.main()
(在同一个程序执行过程中)时,并不会响应这些文件的变化。基于这个原因,我们不推荐在同一个程序中多次调用
pytest.main()
(例如:为了重新执行测试;如果你确实有这个需求,或许可以考虑pytest-repeat
插件)