pytest测试框架之插件(hook函数)开发
参考文档:
https://docs.pytest.org/en/7.1.x/how-to/writing_hook_functions.html
https://juejin.cn/post/7281080420379131958
https://zhuanlan.zhihu.com/p/610804545
pytest 三种插件
pytest 给我们开放了大量的 hook 函数,可以编写插件。pytest 三种插件类型:
- 内置插件:从 pytest 内部 _pytest 目录加载的插件
- 外部插件:通过 pip 安装的插件(比如: pip install pytest-ordering )
- conftest.py 插件:测试目录中的 conftest.py 加载
pytest hook 函数
pytest hook 链接: https://docs.pytest.org/en/stable/reference.html?#hooks
pytest hook 函数也叫钩子函数,pytest 提供了大量的钩子函数,可以在用例的不同生命周期自动调用。 比如,在测试用例收集阶段,可利用 hook 函数修改测试用例名称的编码。
通常我们会把 hook 函数编写在项目的 conftest.py 文件中:
def pytest_collection_modifyitems(session: "Session", config: "Config", items: List["Item"]
) -> None:for item in items:item.name = item.name.encode('utf-8').decode('unicode-escape')item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
运行时,pytest 会优先加载 conftest.py 文件,然后再执行测试用例,这个 hook 函数 pytest_collection_modifyitems 是负责修改收集上来的测试用例的,也就是我们可以将收集上来的 测试用例重新改写它的编码规范,当然也可以修改它的执行顺序。下面我们来看一下 pytest 提供了哪些 hook 函数。
pytest hook函数及执行顺序
pytest 提供了大量的 hook 函数,执行过程中几乎所有的行为都是可以定制的。文字版 pytest hook 执行顺序:
root
└── pytest_cmdline_main├── pytest_plugin_registered├── pytest_configure│ └── pytest_plugin_registered├── pytest_sessionstart│ ├── pytest_plugin_registered│ └── pytest_report_header├── pytest_collection│ ├── pytest_collectstart│ ├── pytest_make_collect_report│ │ ├── pytest_collect_file│ │ │ └── pytest_pycollect_makemodule│ │ └── pytest_pycollect_makeitem│ │ └── pytest_generate_tests│ │ └── pytest_make_parametrize_id│ ├── pytest_collectreport│ ├── pytest_itemcollected│ ├── pytest_collection_modifyitems│ └── pytest_collection_finish│ └── pytest_report_collectionfinish├── pytest_runtestloop│ └── pytest_runtest_protocol│ ├── pytest_runtest_logstart│ ├── pytest_runtest_setup│ │ └── pytest_fixture_setup│ ├── pytest_runtest_makereport│ ├── pytest_runtest_logreport│ │ └── pytest_report_teststatus│ ├── pytest_runtest_call│ │ └── pytest_pyfunc_call│ ├── pytest_runtest_teardown│ │ └── pytest_fixture_post_finalizer│ └── pytest_runtest_logfinish├── pytest_sessionfinish│ └── pytest_terminal_summary└── pytest_unconfigure
pytest_collection_modifyitems
运行测试用例的时候,可能会遇到编码的问题,比如路径里有中文,展示的时候,可能会出现乱码。
需求&源码分析
从源码 site_packages/_pytest/hookspec.py 中(ubuntu18系统通常位置在家目录./lib/python3.6/site-packages/_pytest/hookspec.py)查看 pytest_collection_modifyitems hook函 数的源码:
def pytest_collection_modifyitems(session, config, items):""" called after collection has been performed, may filter or re-orderthe items in-place.:param _pytest.main.Session session: the pytest session object:param _pytest.config.Config config: pytest config object:param List[_pytest.nodes.Item] items: list of item objects"""
该函数是在用例收集完毕之后被调用,可以过滤或者调整测试用例执行顺序。 里面需要传递三个参数,其中 items 是测试的用例对象列表。可以通过遍历 items,然后对每个测试用例的名字重新编码,实现改写编码的效果。
item常用属性
item.nodeid: 返回一个字符串,表示测试用例的唯一标识符。
item.name: 返回一个字符串,表示测试用例的名称。
item.parent: 返回一个节点对象,表示测试用例所属的父级节点(模块、类或测试集合)。
item.function: 返回一个函数对象,表示测试用例的实际执行函数。
item.originalname: 返回一个字符串,表示测试用例的原始名称。
item.location: 返回一个元组,包含测试用例所在文件的路径和行号。
item.keywords:返回一个包含字符串关键字的集合
hook函数本文件夹下生效
创建目录结构如下:
pytest-changcode
├── pytest_changecode
│ └── init.py
└── tests
└── test_code.py
创建插件包 pytest_changecode ,创建测试包 tests
在 tests/test_code.py 文件中添加测试代码,如下:
@pytest.mark.parametrize("name",["张三","李四"])
def test_encode(name):print(name)
在pytest_changecode/init.py 文件中添加改写编码的内容,代码如下:
def pytest_collection_modifyitems(items):
for item in items:
item.name = item.name.encode(‘utf-8’).decode(‘unicode_escape’)
item._nodeid = item._nodeid.encode(‘utf-8’).decode(‘unicode_escape’)
hook函数全局生效
把 hook 函数编写在项目的 conftest.py 文件中
def pytest_collection_modifyitems(session: "Session", config: "Config", items: List["Item"]
) -> None:for item in items:item.name = item.name.encode('utf-8').decode('unicode-escape')item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
运行机制总结
pytest_collection_modifyitems()钩子函数的运行机制如下:
在 Pytest 运行过程中,当测试集被收集完成后,Pytest 会检测到conftest.py文件
中是否定义了 pytest_collection_modifyitems() 函数。
如果 conftest.py中定义了 pytest_collection_modifyitems() 函数,Pytest 会调用该函数,并传递两个参数:config 和 items。config 参数是一个对象,包含了当前测试运行的配置信息。我们可以使用它来访问和修改配置选项,或者获取有关测试环境和命令行参数的信息。
items 参数是一个列表,其中包含了所有收集到的测试项对象。每个测试项对象都包含了测试项的相关信息,如名称、路径、函数/方法定义等。
在 pytest_collection_modifyitems() 函数内部,我们可以根据需要对 items 列表进行修改
。例如,重新排序测试项、过滤测试项、添加标记等。修改 items 列表后,Pytest 将按更新后的顺序和配置继续执行测试。修改后的 items 列表中包含了经过钩子函数处理后的测试项。
总结来说,pytest_collection_modifyitems() 钩子函数在测试集被收集之后被调用,允许我们对收集到的测试项进行自定义修改。通过使用该钩子函数,我们可以根据特定需求实现对测试项的排序、过滤、标记等操作,从而更好地管理和执行测试。
拓展:添加用例id用于生成xml报告
def pytest_collection_modifyitems(items):for item in items:case_id = item.namecase_id = case_id.split("[")[-1].split("]")[0].split("-")[-1]if case_id.isdigit():item.user_properties.append(("id", case_id))
item.user_properties是一个可供用户自定义使用的变量。