前言
在解锁 pytest_configure(config) 的神奇功能:个性化定制你的测试框架这篇文章中,我们在文章最后,抛出了pytest_collection_modifyitems()
钩子函数,那该钩子函数又是啥?怎么用?运行机制?实际运用场景?带着这些疑问,我们一起来探锁pytest_collection_modifyitems()
钩子函数。
pytest_collection_modifyitems()
是啥?
pytest_collection_modifyitems() 是 Pytest 框架中非常有用的钩子函数之一。
看看源码定义:
def pytest_collection_modifyitems(
session: "Session", config: "Config", items: List["Item"]
) -> None:
"""Called after collection has been performed. May filter or re-order
the items in-place.
:param session: The pytest session object.
:param config: The pytest config object.
:param items: List of item objects.
"""
这个钩子函数允许我们在运行测试集之前修改被收集到的测试项(test items),例如重新排序、过滤或添加标记等。
pytest_collection_modifyitems() 函数是在测试集被收集后立即调用的。它接收两个参数,config
和 items
。config
参数提供了关于测试运行配置的信息,而 items
参数则是一个列表,包含了所有被收集到的测试项。
pytest_collection_modifyitems()
如何使用?
我们配合conftest.py
文件进行使用
conftest.py
def pytest_collection_modifyitems(config, items):
# 在这里编写需要执行的代码
pass
我们可以在 pytest_collection_modifyitems() 函数内部编写我们的逻辑代码。该函数接收两个参数:config
和 items
。config
参数提供了关于测试运行配置的信息,而 items
参数是一个包含所有被收集到的测试项的列表。有同学可能还不是不知道items
是啥?这里做一个介绍
参数items
是啥?
一组测试用例被称为一个 "item"。pytest 会根据特定的规则 (默认是以 test_
开头的函数或方法) 自动收集这些测试用例。当 pytest 执行这些测试用例时,它会把它们封装成一个个的 "item",并对它们进行执行和统计结果。
item
有哪些属性?
我们说几个常用属性,用来获取用例相关的信息:
item.nodeid
: 返回一个字符串,表示测试用例的唯一标识符。item.name
: 返回一个字符串,表示测试用例的名称。item.parent
: 返回一个节点对象,表示测试用例所属的父级节点(模块、类或测试集合)。item.function
: 返回一个函数对象,表示测试用例的实际执行函数。item.originalname
: 返回一个字符串,表示测试用例的原始名称。item.location
: 返回一个元组,包含测试用例所在文件的路径和行号。- item.keywords:返回一个包含字符串关键字的集合
我们实际测试一下,看看输出结果,会更加明了
conftest.py
中定义该函数,并输出信息,代码如下:
def pytest_collection_modifyitems(config, items):
for item in items:
print(f"item.nodeid {item.nodeid}")
print(f"item.name {item.name}")
print(f"item.parent {item.parent}")
print(f"item.function {item.function}")
print(f"item.originalname {item.originalname}")
print(f"item.location {item.location}")
然后我们写一个case
进行测试:
test_demo.py
def test_case():
assert True
执行case
输出信息如下:
item.nodeid test_dir/test_demo.py::test_case
item.name test_case
item.parent
item.function
item.originalname test_case
item.location ('test_dir/test_demo.py', 2, 'test_case')
这样输出结果对应着上面的理论知识看,应该比较容易理解了。
常见使用场景
场景一:
重新排序测试项:通过更改 items
列表的顺序,在运行测试时按照特定的排序规则执行测试项。
def pytest_collection_modifyitems(config, items):
# 按照测试项名称字母顺序重新排序
items.sort(key=lambda x: x.name)
场景二:
过滤测试项:根据特定的规则,从测试集中过滤掉不需要执行的测试项。
def pytest_collection_modifyitems(config, items):
# 过滤掉带有 'slow' 标记的测试项
items[:] = [item for item in items if 'slow' not in item.keywords]
场景三:
标记测试项:根据特定的规则,给测试项添加额外的标记。这在动态地为测试项分配标记时非常有用。
def pytest_collection_modifyitems(config, items):
# 给包含 'api' 关键字的测试项添加 'api' 标记
for item in items:
if 'api' in item.name:
item.add_marker(pytest.mark.api)
场景四:
跳过测试项:根据特定的规则,跳过某些测试项的执行
def pytest_collection_modifyitems(config, items):
# 跳过带有 'skip' 标记的测试项
for item in items:
if 'skip' in item.keywords:
item.add_marker(pytest.mark.skip)
案例实现
基本知识了解之后,我们实现一个小需求,我们现在拥有一组带smoke
标记的case
,我们想要在执行全部测试用例时,将smoke
标记的测试移到测试集的最前面。我们看看如何实现:
def pytest_collection_modifyitems(config, items):
smoke_tests = []
other_tests = []
for item in items:
if 'smoke' in item.keywords:
smoke_tests.append(item)
else:
other_tests.append(item)
items[:] = smoke_tests + other_tests
代码还是比较简单的,首先创建了两个空列表:smoke_tests
和 other_tests
,用于存储带有 smoke
标记和其他标记的测试项。然后我们遍历 items
列表,将带有 smoke
标记的项添加到 smoke_tests
列表中,将其他项添加到 other_tests
列表中。
最后一行的 items[:] = smoke_tests + other_tests
将重新排序后的测试项列表赋值给原始的 items
列表,从而实现了将带有 smoke
标记的测试项移动到最前面。
这样,当我们运行pytest
时,所有带有 smoke
标记的测试项都会首先执行,然后是其他测试项。这在快速执行关键的冒烟测试时非常有用,同时保持了灵活性和维护性。
运行机制
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() 钩子函数在测试集被收集之后被调用,允许我们对收集到的测试项进行自定义修改。通过使用该钩子函数,我们可以根据特定需求实现对测试项的排序、过滤、标记等操作,从而更好地管理和执行测试。
最后
不得不感叹pytest
的强大。各种钩子函数满足你的不同需求场景。回到pytest_collection_modifyitems
,我们使用时还是要注意:pytest_collection_modifyitems()
函数中的修改操作会直接影响到测试项的执行顺序和状态。因此,在使用该钩子函数时,请确保了解并测试了修改后的结果,以避免出现意外情况。